Trifork Blog

Automated functional testing with WebDriver

August 5th, 2009 by
|

There is nothing nicer than having a functional test suite that checks if your application is still working as it should. It is even nicer to run these tests and see what is happening, while the tests are being executed. Two of the tools that give you this opportunity are Selenium RC and WebDriver.

In the project that I’m working on I use WebDriver to create functional tests. In this post I will try to give you an impression of why we switched from using Selenium RC to WebDriver and how we used WebDriver to test a Content Management System (CMS) and helped us making understandable tests.

Switching from Selenium RC to WebDriver

As I mentioned in the introduction, we switched from using Selenium to WebDriver. You might think why would you write your tests while you can record them with Selenium IDE. This is a great tool, but was not useful in our situation, because the CMS uses auto generated id’s for their elements. This means that a recorded script with Selenium IDE would not work in a continuous build. So in order to create tests we had to write them our self. And when writing your tests WebDriver has a few advantages over Selenium.

With Selenium you need to start a server to execute the integration tests, but with WebDriver you only need to pick a driver and you are good to go. You basically just create a unit test without any dependencies of a server.

One of the other advantages of WebDriver is the clear API. In the next sections I will explain how the API of WebDriver works.

Scenario

Before I continue about WebDriver, I want to explain the test case scenario that I’m using in this post.

In the project we create content that always needs to be present at a specific place in the CMS. An example could be a help or contact page. As the content is expect to be in a specific place, we want to test if the navigation to this content is as it should be. But this is not enough and therefore we would also like to test if the content is present.

Drivers

When you start with WebDriver you have to make a choice which driver you want to use. WebDriver currently supports four different drivers: HtmlUnitDriver, FirefoxDriver, InternetExplorerDriver and the SafariDriver.

The HtmlUnitDriver is really fast, but does not allow you to see what is actually happening. This can become interesting when you just want to execute the test and get an overview of the results. I will not go over the differences between these drivers, but when you want to display the steps that are executed you have to chose one of the other three drivers. In our project we have used the FirefoxDriver as we interacted with a Linux environment.

So the first thing that is needed to create a test is an instance of one of the drivers:

 WebDriver driver = new FirefoxDriver(); 

Now we have to tell the driver to navigate to a page. In our case this will be the login page:

 driver.get("http://localhost:8080/cms/login");

When the page is loaded the driver can do a few things to interact with the page. One of the things is that it can find elements on that page. The WebDriver documentation provides an example that fits the situation very well. As most of the login pages it contains a password and username input field. In order to find the password element on our login page WebDriver provides the following options:

 WebElement element;
element = driver.findElement(By.id("passwd-id"));
element = driver.findElement(By.name("passwd"));
element = driver.findElement(By.xpath("//input[@id='passwd-id']"));

One of the common parts for each of our tests was the login procedure. As everyone can understand that you don’t want to duplicate this code, WebDriver introduces the Page Objects.

Page Objects

Page Objects simply models the UI areas that you want to interact with in your test. This reduces the amount of duplicated code and means that if the UI changes, the fix need only be applied in one place.

For our tests we introduced a LoginPage object that allows you to login with a specific username and password.

 LoginPage loginPage = new LoginPage();
CmsHomePanel homePanel = loginPage.login("username", "password");

The implementation of the login method looks like this:

 public CmsHomePanel login(String username, String password) {
    WebElement usernameElement = driver.findElement(By.name("username"));
    usernameElement.sendKeys(username);

    WebElement passwordElemement = driver.findElement(By.name("password"));
    passwordElemement.sendKeys(password);

    WebElement submitButton = driver.findElement(By.name(":submit"));
    submitButton.submit();     

    return new CmsHomePanel(helper);
}

As you may have noticed in the code above a different Page Object is returned. After each login we always end up on the home panel of the CMS. We named this Page Object a panel because the CMS is based on panels.

There is also a different way of lookup up an element and that is via annotations. The annotation based lookup uses the PageFactory to initialize the PageObject. If we would look up the username element in de previous code snippet via annotations it would look like this:

 @FindBy(how = How.NAME, using = "username") 
private WebElement username 

Because the CMS uses generated id’s for their elements it becomes quite difficult to find the elements based on an id or name. WebDriver offers a solution by finding the elements based on xpath. Going further with the example test, we now want to test the navigation.

 
CmsDocumentPanel documentPanel = homePanel.clickOnDocumentTab();  
assertTrue(documentPanel.isFolderPresent("navigation")); 

documentPanel.clickOnFolder("navigation");  
assertTrue(documentPanel.isFolderPresent("main-navigation")); 

documentPanel.clickOnFolder("main-navigation"); 

Before we can test the navigation we have to click on the document tab, so that we could navigation through the folders. This returns once again a Page Object, namely CmsDocumentPanel. This object has a method that indicates if a folder is present and a method to click on the folder. The implementation of the isFolderPresent(String folderName) and the clickOnFolder(String folderName) method is done with xpath.

Now that we have tested that the navigation folders are in place, it is time to check if the content is actually present. This is also done via a method on the documentPanel Page Object:

 
assertTrue(documentPanel.isDocumentPresent("Contact")); 
assertTrue(documentPanel.isDocumentPresent("Help")); 

The Page Objects are also responsible checking the state of the page.  As you can see in the example code this is done by the methods isFolderPresent() and isDocumentPresent(). These methods gives you the possibility to make assertions in the test. Notice that the assertions are made in the test and not in the Page Object!

Conclusion

I have explained why we preferred to used WebDriver instead of Selenium RC and the way we used the WebDriver API, which resulted in clear and readable tests.

WebDriver offers a lot of possibilities for creating functional test. Although sometimes we had to pull off some tricks to find the correct element, WebDriver made it possible. By making good use of the API it will help you to test your web application in nice and understandable way. Even for other people than developers.

11 Responses

  1. August 5, 2009 at 22:08 by Mr. Hericus

    We use Selenium RC significantly to automate the testing of our GUI, but I can see some of the advantages that you talk about. I’m curious now to look into WebDriver based on your recommendation to see how it could be adapted to work with our web GUI.

    Thanks for the information and the post!

  2. August 6, 2009 at 10:07 by Stephan

    You mention that an advantage of Webdriver above Selenium is that you can workaround the generated IDs by using an xpath expression. However i do not see a usage of paths in your examples. Am i missing something?

  3. August 6, 2009 at 11:28 by Roberto van der Linden

    Hi Stephan,

    No, I left the expressions out. But basically it comes down to finding the correct element by using the class names and relative element locations. Sometimes I have used the ids of an element that wasn’t generated.

    This has the disadvantage that your test code is brittle, because it is tightly coupled to the component layout of the CMS.

    Cheers,
    Roberto

  4. October 4, 2009 at 05:55 by sameera

    a lot needs to be done to this tool …
    dosent have much support for handling pop ups properly.

  5. January 18, 2010 at 04:23 by JerraraJohn

    What does the “helper” do in this code:

    return new CmsHomePanel(helper);

  6. January 18, 2010 at 22:28 by Roberto van der Linden

    The helper just provides us with a few convenience methods. In this case it is used to call a method that lets the test wait for a second, so it has a little more time to complete the call.

  7. March 3, 2010 at 15:46 by Paulo

    What’s your opinion on the merging of WebDriver and Selenium ?? The new Selenium 2.0 offers this feature.

  8. March 20, 2010 at 16:40 by Madhu

    Any Example using WebDriver where id of elements change dynamically

  9. March 25, 2010 at 07:58 by David Clark

    I like web driver, but the application that I am testing has a 10 – 15 sec delay between a submit and the next page being loaded. I find it frustrating that I have no way of knowing in webdriver when the page has been loaded. I have been putting in a wait loop till the current url has change. (Or am I missing something?)

  10. September 9, 2011 at 12:49 by Sandip Ganguli

    Hi Guys,

    I am exploring the web driver APIS from last few days. What I found actually
    the problem which selenium API have, web driver also have the same. Take an
    example when elements ids are run time generated then how to locate that element ?
    Page object does not solve the automatic identification of those elements, like selenium-RC. So for both cases u need to write some generic approach which will
    convert the current html page as object model, and provide some sort of interface
    to locate those elements.

    So from that perspective Selenium-RC is much powerful than Web Driver, since its has
    rich set of APIs(so called complex) and supports all browsers many platforms …..

    I prefer Selenium-RC over Web Driver.

  11. October 19, 2011 at 08:11 by angelle

    Why is that it becomes quite difficult to find the elements based on an id or name. .