Unit testing web applications is relatively hard, because you usually have quite a lot of layers: starting from the database, over PHP/Python/Ruby, the web server, the browser and HTML up to Javascript. Traditional unit testing hooks into the code at the PHP/Python/Ruby level. It does quite a good job at testing this layer of web applications. There also exist testing frameworks that allow you to unit test Javascript. Alas, none of these solutions test the whole experience that the end user has. This is where functional unit testing comes into play.

Selenium, a framework that supports functional unit testing of web applications, simulates a user clicking links, filling out forms and submitting them. It can determine whether a blog post appeared in a list, after you used a form to submit it. It can test whether your AJAX screenname availability check works. It supports cookies. And all that because it runs directly in your browser. The Javascript that runs in your browser receives commands like “click the submit button” or “wait for the spinner to appear” or “make sure this error message didn’t appear on the page”.

Selenium comes in several flavours: one is Selenium Core, which runs directly and only in the browser. As a language for writing tests it supports only selenese, which can express only a simple sequence of actions and tests. This is of course too little for most web applications. But there’s a flavour of Selenium that can be integrated with traditional unit testing frameworks like SimpleTest (for PHP) and PyUnit: Selenium Remote Control.

Selenium Remote Control is essentially a server written in Java with two functions: it acts as a proxy server for the browser and takes commands via HTTP that will be executed in the browser. The proxy server is needed because browsers rightfully don’t allow Javascript from one site to run on another site. Read Selenium Remote Control: The Same Origin Policy if you’d like to know more.

Selenium Remote Control comes with client drivers for Python, Ruby, Perl and Java – a PHP client driver has not made it in the official release yet.

To start using Selenium Remote Control for doing functional tests of your web application, download the current release (http://openqa.org/selenium-rc/download.action), unpack it and start the server:

wget selenium-remote-control-0.8.1.zip
unzip selenium-remote-control-0.8.1.zip
cd selenium-remote-control-0.8.1/server
java -jar selenium-server.jar

This starts the two servers: the proxy server for the browser and the server that takes the commands. Then write a unit test case, using one of the client drivers provided in the archive. I’ll use Python here:

mkdir functional-tests
cd functional-tests
cp path/to/selenium-remote-control-0.8.1/python/selenium.py .

and put this in a file test-login.py

import unittest

class LoginTest(unittest.TestCase):
seleniumHost = 'localhost'
seleniumPort = '4444'
browserStartCommand = '*firefox /usr/lib/firefox/firefox-bin'
browserURL = 'http://community'

def setUp(self):
self.selenium = selenium('localhost', '4444', '*firefox /usr/lib/firefox/firefox-bin', 'http://www.example.com')
self.selenium.start()

def tearDown(self):
self.selenium.stop()

def testLogin(self):
self.selenium.open("/login")
self.selenium.type("name=username", "john")
self.selenium.type("name=password", "johnspassword")
self.selenium.click("id=Submit")
self.selenium.wait_for_page_to_load(15000)
self.failUnless(self.selenium.is_text_present("Login successful"))

if __name__ == '__main__':
unittest.main()

Then run

python ./test-login.py

and watch selenium opening a Firefox instance for you, loading the login page, typing username and password, submitting the form and finally checking for the message about successful login. After that Firefox is closed and PyUnit reports that the test ran successfully.

Links