News from Feb 19, 2010

  2010/02/19
WebDriver drives integration tests to the next level!
Last changed: Feb 23, 2010 09:04 by Luiz Ribeiro
Labels: testing, webdriver

WebDriver is a new framework by Google for writing web automated tests. It is being merged with Selenium by OpenQA into Selenium 2.0, but as of Feb 19th, 2010, it is already available at http://code.google.com/p/selenium/ while the development is still in progress. Some of the advantages of WebDriver are a better and object-oriented API, use of browser-specific mechanisms to execute the tests and better support for advanced cases like drag and drop and dealing with pop-ups or multiple windows.

Using WebDriver is simple, there's no need to start any server or anything. All that it takes is to instantiate and use any of the WebDriver interface implementing classes. There's a driver for each of the supported browsers (the current drivers are Firefox, Internet Explorer, Chrome and HTMLUnit):

Running WebDriver tests with Firefox
    WebDriver driver = new FirefoxDriver();
    driver.get("http://localhost:8080/mywebsite");
    driver.findElement(By.id("username_field"));

Each driver uses a different approach to simulate the user's interactions with the browser, contrary to Selenium that always uses Javascript. This means that the tests run in the most native way in each browser, which guarantees better performance and flexibility, but as the API is still the same, writing tests that have to run in many different browsers becomes simple
Currently, as I am writing this, the Firefox driver is the most advanced one in terms of implementation. You can check the development road map here: http://code.google.com/p/selenium/wiki/RoadMap

The WebDriver object-oriented API makes the code naturally more maintainable. Plus, it also makes it easier to access the web elements, their children and their properties, reducing the need to use xpaths:

WebDriver code example
    String username = "admin";
    String password = "admin";

    WebDriver driver = new FirefoxDriver();

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

    driver.findElement(By.id("os_username")).sendKeys(username);
    driver.findElement(By.id("os_password")).sendKeys(password);

    driver.findElement(By.id("loginButton")).click();

    assertEquals("Dashboard", driver.getTitle());

    WebElement recentTable = driver.findElement(By.id("recentlyUpdated"));
    List<WebElement> recentTableRows = recentTable.findElements(By.tagName("tr"));

    assertEquals(10, recentTableRows.size());

Notice how the findElements method searches for the elements children of the recentTable element.
It looks even better if used with the PageObjects pattern!

Talking about PageObjects, WebDrivers also offers a PageFactory class with auto binding for web elements, which makes it even more fun to use the pattern:

The same test case, using PageObjects and WebDriver's PageFactory
public class LoginPage {

  private WebDriver driver;

  @FindBy(id = "os_username")
  private WebElement usernameField;

  @FindBy(id = "os_password")
  private WebElement passwordField;

  // automatically binds by id using the field's name
  private WebElement loginButton;

  public LoginPage(WebDriver driver) {
    this.driver = driver;
    driver.get("http://localhost:1990/confluence");
  }

  public Dashboard login(String username, String password) {
    usernameField.sendKeys(username);
    passwordField.sendKeys(password);
    loginButton.click();
    return PageFactory.initElements(driver, Dashboard.class);
  }
}

public class Dashboard {

  private WebDriver driver;

  @FindBy(id = "recentlyUpdated")
  private WebElement recentTable;

  public Dashboard(WebDriver driver) {
    this.driver = driver;
    if (!"Dashboard".equals(driver.getTitle())) {
      throw new IllegalStateException("Dashboard page expected");
    }
  }

  public int getRecentlyUpdatedRowsCount() {
    List<WebElement> recentTableRows = recentTable.findElements(By.tagName("tr"));
    return recentTableRows.size();
  }
}

And our test:

    LoginPage loginPage = PageFactory.initElements(driver, LoginPage.class);
    Dashboard dashboard = loginPage.login(username, password);
    assertEquals(10, dashboard.getRecentlyUpdatedRowsCount());

Isn't it cool?

Get to know more about WebDriver at http://google-opensource.blogspot.com/2009/05/introducing-webdriver.html and http://code.google.com/p/selenium/
A more complete manual decribing also some more advanced features can be found here: http://seleniumhq.org/docs/09_webdriver.html

Have fun!

Posted at 19 Feb @ 5:10 PM by Luiz Ribeiro | 7 Comments