Thanks to a few ruby libraries it’s pretty simple to create a testing tool using ruby that can send post and get requests to a webserver and then parse the result. The main benefit of using this kind of test approach is when you want to run a suite of tests in a continuous integration build and get fast feedback if something has changed or broken.
Some people prefer to go for tools that fire up a browser and then drive the browser – tools like:
There are many others but the main downfall to this type of tool is that it is often really slow, especially when people start churning out a lot of tests. Some people go for a combined approach – they run post/get type tests in the build on a day to day basis and then run the same tests but with the browser once or twice a day or overnight – this approach seems quite common with webdriver the java based tool. It lets you choose how you want to run your tests – headless or with a browser or another runner of your choice.
So the approach I’m going to take here is to create a DSL (Domain Specific Language) type of test suite. By DSL I mean it will allow you to write tests using language that is appropriate to the domain we are working with. This is going to be really simple and we are going to be writing tests for Google.
Lets install some gems we will need:
- sudo gem install mechanize rspec syntax
Now lets write some code. Make a directory called GoogleTest and inside create the file google.rb
class Google
FIELDS = {
:search_field => 'q',
:location => 'meta'
}
def initialize(agent,page=nil)
@page = page
@agent = agent
@form = @page.forms.first unless @page.nil?
end
def navigate
@page = @agent.get("http://www.google.co.uk")
@form = @page.forms.first
end
def search_term=(value)
set_textfield(:search_field, value)
end
def search_term
get_textfield(:search_field)
end
def location=(value)
set_radiobutton(:location,value)
end
def location
get_radiobutton(:location)
end
def google_search
button_with_value("Google Search")
end
private
def set_textfield(field_name, value)
@form.field_with(:name => FIELDS[field_name]).value = value
end
def get_textfield(field_name)
@form.field_with(:name => FIELDS[field_name]).value
end
def set_radiobutton(field_name, value)
@form.radiobutton_with(:name => FIELDS[field_name]).value = value
end
def get_radiobutton(field_name)
@form.radiobutton_with(:name => FIELDS[field_name]).value
end
def button_with_value(value)
button = @form.button_with(:value => value)
@page = @agent.submit(@form, button)
end
end
As you can see here we are mapping out the fields available on the google search form with appropriate methods. This will help us to write rspec specifications more relevant to the domain of google. So now lets create a spec file in the GoogleTest directory – google_search_spec.rb
require 'rubygems'
require 'mechanize'
require File.expand_path(File.dirname(__FILE__) + '/google.rb')
describe Google do
before(:all) do
@agent = WWW::Mechanize.new
@agent.read_timeout = 240
end
before(:each) do
@google = Google.new(@agent)
@google.navigate
@search_term = "software testing"
end
it "should perform a search with the supplied search term" do
@google.search_term = @search_term
result = @google.google_search
number_of_matching_links(result,@search_term).should have_at_least(20).items
end
it "should perform a search with pages from the uk" do
@google.search_term = @search_term
@google.location = "cr=countryUK|countryGB"
result = @google.google_search
number_of_uk_links(result).should have_at_least(7).items
end
def number_of_matching_links(result,term)
result.search("a").collect{|link| link if link.inner_text.match(/#{term}/i)}.compact
end
def number_of_uk_links(result)
result.search("a").collect{|link|
link if link.attributes['href'].text.match(/\.co\.uk/) and !link.attributes['href'].text.match(/google/)}.compact
end
end
Rspec lets you describe the general area you want to test – in this case its our Google class. Next we setup variables that we will use throughout the tests. We instantiate a new mechanize object that has all the http connection stuff. I have set the timeout just to show how its done – it’s not really needed in this example.
Next we setup variables that we want to use in each of the tests that get instantiated freshly each test – again this was not strictly required for this example but I wanted to show you this feature of rspec for future reference.
Now we get down to some testing. We set the search term on the google object and then run the google search and check that the number of links returned is at least 20. In the second test we do the same but we set the location radio button to use pages from the uk only (If you are in a different country you might need to tweak the urls and values a little). Finally we check that the result contains at least 7 links that have co.uk in them.
The nice thing about this approach is that we can build up some common language about how to perform google searches and if google decides to change the structure of its webpages we can keep the test exactly the same but change the supporting classes behind.
Lets run the tests and see the results on the command line:
- spec google_search_spec.rb
and you should see:

You can also run a test on a specific line number which is quite useful:
- spec -l18 google_search_spec.rb
And for running in a continuous integration build you can get it to print an htlm report:
- spec -f html:./result.html -f p google_search_spec.rb (the p just gives cmd output as well)

Lets make it fail just to prove it’s actually working. Edit the spec and change the expected result to 300:
it "should perform a search with the supplied search term" do
@google.search_term = @search_term
result = @google.google_search
number_of_matching_links(result,@search_term).should have_at_least(300).items
end
Run it again and see the result:

This approach is great for fast feedback but it does have a few shortcomings when dealing with ajax based websites. Handling ajax post backs isn’t too difficult as we can just submit the form again to get the updated call back data. Trying to test pages that have alot of dynamic javascript is much much harder. A good example on the google search page is the I’m Feeling Lucky button – it uses javascript to take you straight to the first search result in the list. A workaround for this case is to submit a get request to the server something like this:
http://www.google.co.uk/search?hl=en&q=software testing&btnI=I%27m+Feeling+Lucky&meta=&aq=f&oq=
Obviously this was a really simple example to give you the general idea. I have implemented a much more complex suite of tests based on this approach at a client where we have over 300 tests of this nature running on every svn checkin in a cruise build pipeline. The tests take around 30 minutes to run and they run multithreaded.
Latest Comments
"Highly descriptive article, I e..."
rc heli / 31.Dec.2011 at 11:04pm
"Glad to visit this blog, keep i..."
rc helicopters / 28.Dec.2011 at 07:16am
"Very nice. Used to access
nick xidis / 20.Jul.2009 at 11:29pm
"Thanks Mark – I have put ..."
kingsley / 24.Jun.2009 at 11:10am
"Nice work! Could you publish th..."
mark yoon / 10.Jun.2009 at 05:03pm