Testing web interfaces with Selenium

So not too long ago I wrote and committed some Selenium tests for oVirt which can be found here and wrote some info up on the topic, here. I’ve since recieved a few emails asking about the topic so I figure I’d share some introductory points.

For those who don’t know Selenium is a system to test web interfaces. It is javascript friendly, and allows you to manually or automatically do things like click elements, enter text, and verify page contents. The Selenium project consists of several components, an IDE firefox plugin which you can use to locally run tests, Remote Control (RC) clients for many languages which can run tests remotely, and a Selenium server to actually run the tests. I will be focusing on setting up a Selenium server and using the Ruby Selenium RC client to test a rails application. (more after the jump)

To start off, you will need to obtain and run the Selenium Server, provided as a JAVA jar which can be found here. Once you’ve downloaded the version you want to use, and extracted the archive, cd into the selenium-remote-control/selenium-server/ directory and run it like so “java -jar selenium-server.jar”. I’m not sure if it will work with other Java implementations, but for the record I am using Sun’s official Java interpreter. Append “-interactive” to the command line parameters to enter interactive mode in which you can specify commands manually and see their output. Other server command line options can be found here. For convenience and to bring the Selenium server to be more inline with the system services interface, I wrote a Selenium server init script, which can be found here, that you can place into /etc/init.d/, so that you can start / stop by running the ‘service’ command and configure it with ‘chkconfig’.

There are a few caveats that I’ve noticed with my experiences with launching firefox via Selenium to test oVirt, that might be good to know. Should you log into the site in an automated fashion via your Selenium tests you will most likely get a popup in firefox stating you are about to log into this site as xyz, are you sure?. Or if Firefox quits unexpectidly / crashes you might get something along the lines of your session crashed unexpectedly, restore?. This of course interferes with the automated abilities nature of Selenium, but you can get around these limitations by adding the following to your firefox profile (respectively):

     user_pref("network.http.phishy-userpass-length", 255);
     user_pref("browser.sessionstore.enabled", false);

To instruct the Selenium Server to use your Firefox profile instead of an autogenerated one, specify the -firefoxProfileTemplate <dir> command line option, or set the corresponding variable in my init script. Finally the last step on the server end is making sure it is accessible, eg by running the client on the same machine that it is on, opening the default tcp port 4444, or changing the server port to an open one via the -port <port> command line option. Of course the last item was the ‘final’ one because it should go without saying that the Selenium Server itself needs to be able to access the web server that the web interface you want to test is hosted on as well as the local web browser executable that you want to use (whether it be Firefox, IE, or another). ;-)

The next step in this whole process is to setup your testing system to use the Selenium RC client. These exist for many of the big languages, and is provided in the same package from which you got the selenium server jar from. For this example, I’ve extracted ‘selenium.rb’ from that zip file and placed it in the test/ directory of my rails application. Inside the ‘test/functional/’ directory I’ve created an ‘interface_test.rb’ module (this will be picked up by ‘rake test’, eg when the ruby / rails test system is invoked) which contains the necessary code to run my interface tests. Use the link to the oVirt repository that I pasted at the very top to follow along as I explain this module.

After some preliminary stuff, we get to the standard ‘setup’ function, run before each test, in which this config file is loaded and used to populate some local variables to be used in connecting to the Selenium Server. This is done via a call to Selenium::SeleniumDriver.new(ipaddress, port, browser, siteurl, timeout) where ipaddress and port are that of the Selenium Server, browser is the name (and optional location) of the browser which selenium is to run the tests with (firefox, ie, etc), siteurl is the url of the web server which hosts the content selenium is going to test, and timeout is the time which commands will be permitted to delay before a test fails. The browser is then started, and the url which we are trying to access is opened.

In the ‘teardown’ method, executed after every test is completed, we simply see a resolution to this process as the browser is shutdown and stopped. In the actual tests below we see various aspects of the Selenium testing API, such as browser.gettitle() which returns the title of the browser, browser.click(selector) which clicks an element of the page given by the indicated selector, browser.type(selector, text) which types the specified text into the specified element, browser.gettext(selector) which gets the text of the specified selector, and browser.waitforcondition(javascript_condition, timeout) which waits for a javascript condition to evaluate to true before proceeding or a timeout to occur before raising an exception, whichever comes first (useful for testing dynamic components which can take a little while to load). Selectors can be specified by any number of means, perhaps the most powerful of which is XPath, which I wrote an article on a little while back.

And thats about it. If you followed the previous steps, and nothing went awry, you should be running some interface tests against your web application. Good luck!