Friday, May 22, 2009

Selenium In Rails with Fixtures

Selenium is a framework for acceptance testing, designed for web applications. With selenium you can run your tests on all popular browsers.

Now a days web applications are consist of a lot of UI logic, animations, ajax calls. Only unit test and functional test does not ensure the stability of your application any more. A small change in Javascript can cause instability in your application. Tiny changes can break the contract of browser compatibility. A simple miss spelled link can give you a nightmare.

Selenium gives you the power to run automated acceptance test codes in real browsers. As it supports all popular browsers you can easily test browser compatibility of your application.

Key Features

  • Selenium IDE provides facility to record test script with ease and export the script to your preferred language(Java, C#, ruby, Perl, PHP etc)
  • Write one test script and run in all popular browsers and OS
  • Selenium Grid facilitates to run test codes parallely in multiple server, which pace up the execution of test code.

Setup

Install Selenium client

gem install selenium-client

Download Selenium Remote Control from here.

You will need Java installed on your machine to run Selenium Remote Control.

I write a script to start Selenium Remote Control, which is like.

cd E:\selenium\selenium-remote-control-1.0-beta-2\selenium-server-1.0-beta-2
java -jar selenium-server.jar

Use Firefox and Selenium IDE(a Firefox plugin) to capture the actions and convert it to ruby code.

XPath Checker and Fire Finder are two useful Firefox plugin to find and locate elements using xpath.

Record actions

Start Firefox, go to the root url, from where you are going to start test. Go to tools tab and start Selenium IDE.

Now as you browse through the site Selenium IDE will record the actions. After you have finished a scenario, you can stop recording and export test case as ruby file from File menu.

Selenium IDE generates is an old rails style code and it uses old selenium client driver. Here is a sample integration code that uses rails 2.3.2 style and new selenium client driver.Now to run your test code as you have noticed, you need mention the root url of the site you are testing.

require 'test_helper'
require 'selenium/client'

class PostsIntegrationTest < ActionController::IntegrationTest
fixtures :all

def setup
@browser = Selenium::Client::Driver.new(
:host => "localhost",
:port => 4444,
:browser => "*firefox",
:url => "http://localhost:3000",
:timeout_in_second => 60
)

@browser.start_new_browser_session
end

def teardown
@browser.close_current_browser_session
end

test 'the truth' do
@browser.open "/posts"
@browser.click "link=New post", :wait_for => :page
@browser.type "post_title", "test"
@browser.type "post_title", "Testing with Selenium"
@browser.type "post_body", "bla... bla..."
@browser.click "post_published"
@browser.click "post_submit", :wait_for => :page
assert_equal "Testing with Selenium", @browser.get_text("xpath=/html/body/p[2]/span")
end

end
Here is the trick. Run your application in test environment. So the application will use test database. As test database is prepared automatically by rails, you can use the test fixtures as the input set of you test codes. And it will be automatically reset to the fixture set for each test methods.

How to organize

1. Dry and reuse

Acceptance test codes are directly dependent on view code. So you need to reuse test codes as much as possible. If you don’t a simple change in view will make you life hell.

2. Separate your actions to methods, so that you can reuse

def login(user_name, password)
@browser.open "/"
@browser.type "user_name", user_name
@browser.type "password", password
@browser.click "css=.btn[name='commit']", :wait_for => :page
end

def login_as_developer()
login(users(:developer).user_name, '123')
end

Now you can call login method to login to the system. And do not put assertions in these methods. This way you can even test the failure path.

3. Now call these basic methods and do assertion. This will make your code readable.

test 'login as a developer with valid credential' do
login_as_developer()
assert_url('/dashboard/show')
end

Note: Well there is no such thing as assert_url in selenium client to assert the current relative url.You can copy the following code and put it in your test helper.

def assert_url(url)
assert_equal(url, @browser.get_location().match(/.+\/\/[^\/]+(.*)/)[1])
end

4. Use a different helper file for common methods. Don’t use the common test helper, as selenium test are quit different from unit and functional test of rails.

It does not uses mock request and response. So you can not use selenium methods else where. That is why it is better to separate the selenium test helper.

5. Use selenium-client, so that you can use different wait_for methods, which are very useful. See the documentation here.

No comments: