Effective Selenium testing

Selenium is the de facto standard for testing web applications. In this article I’m going to cover a number of techniques for improving your Selenium tests. The article is suitable for you if:

  1. You already know the basics of Selenium and are happy to write tests in Java (or one of the other Selenium API languages).
  2. You want to progress from having a few simple tests to building a large test suite for a complex web application with javascript and ajax.

The techniques I’ll cover are:

  1. Organising your tests – using the page controller design pattern
  2. Understanding the Selenum 2 webdriver and implicit waits
  3. Detecting when web elements are available
  4. Knowing when ajax calls have finished
  5. Taking screenshots for failing tests

The code snippets are in Java, but the Selenium API is available for a wide range of other languages (Ruby, Python, C#, PHP and Perl).

Organising your tests – using the page controller design pattern

If you write a large number of tests, when the application under test changes, you could face a maintenance nightmare. How do you avoid this? The answer is the page controller design pattern. This means that you have a Java class for each page of the application. It knows how to control that web page. i.e. it knows how to identify the elements on the page, how to select them etc. Your test classes don’t know anything about the web page. They don’t include any html ids or other selectors, they simply invoke methods on your page controller. If that web page changes, you only need to update your code in a single place. Code that used to look like this:

driver.findElement(By.id("checkoutButtonCartPage")).click();
driver.findElement(By.id("homeDeliveryButton")).click();
driver.findElement(By.id("postCodeEntry")).sendKeys("AB12 3CD");
driver.findElement(By.id("postCodeSubmitButton")).click();

becomes:

cartPageController.goToCheckout();
checkoutPageController.selectHomeDelivery();
checkoutPageController.postCodeLookup("AB12 3CD");

Understanding the Selenium 2 webdriver and implicit waits

One of the tricky aspects of Selenium testing is knowing when pages have loaded and when elements have appeared on pages. In Selenium 2 the webdriver includes some helpful functionality in this area, but it is important to understand what it does and doesn’t do. When you call the findElement method on the webdriver, if it cannot find the element on the page, it doesn’t fail immediately. Rather, it polls the page every 500ms until it reaches its timeout period. You can set this timeout period by calling webdriver.manage().timeouts().implicitlyWait. However, misunderstanding the webdriver polling functionality can cause confusion. The web driver findElement method returns a web element as soon as it finds it in the browser DOM. However, that doesn’t necessarily mean that you can interact with that web element. All elements have a display property. If the current CSS rules are not displaying that element, you can’t interact with it. The web driver won’t time out, rather it will find the element and return it to your code, which will promptly fail with an exception that explains you can’t interact with the element. A common example of this is a web page where pressing a button triggers a form to appear. Usually this form will already be in the web page, it is just hidden by CSS. Hence, if your Selenium test presses the button to make the form appear, if it proceeds to try and interact with the form too quickly, the CSS won’t have been switched over to display the form by the time the web driver locates it, and your test will fail. Thankfully, Selenium has additional functionality that can help us, which I’ll explain in the next section.

Detecting when web elements are available

We’ve just seen that it isn’t enough for a web element to be present in the browser DOM for us to interact with it, it needs to be visible as well. How do we detect this with Selenium? Well, the web driver allows you to poll for a specific condition to become true, by using the WebDriverWait class. It includes a number of standard conditions, of which visibility is one. This makes it easy to code up a helper method that will only return an element when it is visible:

public WebElement getWhenVisible(By locator, int timeout) {
	WebElement element = null;
	WebDriverWait wait = new WebDriverWait(driver, timeout);
	element = wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
	return element;
}

Is this enough? Well, not necessarily. Interactive web elements also have an “enabled” property. e.g. if you want to show a checkbox but not allow the user to be able to change it, you set enabled to false. If you want to be certain that you can click an element, Selenium has another standard condition for this which you can make use of. e.g.

public void clickWhenReady(By locator, int timeout) {
	WebDriverWait wait = new WebDriverWait(driver, timeout);
	WebElement element = wait.until(ExpectedConditions.elementToBeClickable(locator));
	element.click();
}

For the full range of expected conditions, refer to the Selenium javadoc:
ExpectedConditions

Knowing when Ajax calls have finished

If your test triggers an ajax call, you don’t want to carry on until that call has finished, but how do you know? You might be okay just to use one of the wait conditions above, but this isn’t a very clean approach, and it only works for ajax calls that result in changes to the browser DOM. i.e. it won’t work for calls that simply send data to the server without any changes in the browser html. Wouldn’t it be nice to be more certain when the call was finished? Well, if you are using jQuery to make your ajax calls, you can do so by exploiting the fact that most web driver implementations can run javascript. jQuery keeps a count of how many ajax calls are active in its jquery.active variable. Here’s an example of a helper method to wait for an ajax call to finish:

public void waitForAjax(int timeoutInSeconds)  {
  System.out.println("Checking active ajax calls by calling jquery.active");
    try {
      if (driver instanceof JavascriptExecutor) {
	JavascriptExecutor jsDriver = (JavascriptExecutor)driver;
 
        for (int i = 0; i< timeoutInSeconds; i++) 
        {
	    Object numberOfAjaxConnections = jsDriver.executeScript("return jQuery.active");
	    // return should be a number
	    if (numberOfAjaxConnections instanceof Long) {
	        Long n = (Long)numberOfAjaxConnections;
	        System.out.println("Number of active jquery ajax calls: " + n);
	        if (n.longValue() == 0L)
	       	  break;
	        }
            Thread.sleep(1000);
	    }
	}
	else {
		System.out.println("Web driver: " + driver + " cannot execute javascript");
	}
}
	catch (InterruptedException e) {
		System.out.println(e);
	}
}

Of course, this example could be rewritten to use the WebDriverWait format if you wish.

Taking screenshots for failing tests

One of the golden rules of good testing is that you should be able to diagnose why a test failure has occurred without rerunning the test. For Selenium, as well as having good assertions and debug output within the tests, it is very useful to take a screenshot when a failure occurs. If you are using JUnit for your tests, a neat way of doing this is to use a JUnit rule to take the screenshot. A good write up of how to do this is here:

http://blogs.steeplesoft.com/2012/01/grabbing-screenshots-of-failed-selenium-tests/

Summary

In this article we’ve seen:

  • How to organise your test suite by using the page controller design pattern
  • How the Selenium web driver works and how to write tests for web applications with javascript and ajax
  • How to take screenhots for failing tests

For more information about the web driver and testing design patterns, see the Selenium docs:
http://seleniumhq.org/docs/index.html
If you’re interested in using CruiseControl to automate your tests:
Automating Selenium testing with TestNG, Ant and CruiseControl
If you’d like to learn about using XPath for complex element location:
Writing XPath selectors for Selenium tests

This entry was posted in Selenium, Testing, Uncategorized and tagged , . Bookmark the permalink.

One Response to Effective Selenium testing

  1. paul C says:

    Thanks for your post. even an year after, this was extremely helpful, as i had a lot of problems with NoSuchElementException. I hope you get the time to update it(if needed) and post new topics about it. ty

Leave a Reply

Your email address will not be published. Required fields are marked *

502,136 Spambots Blocked by Simple Comments

HTML tags are not allowed.