Page Objects in Selenium 2.0

Want to learn more about WebDriver? What do you want to know?

So you’ve written your first Selenium 2.0 test, but is that really the right way to build tests? In the second article in this series we’ll look at the difference between test specification and test implementation and how Selenium achieves this with page objects.

Specification vs Implementation

There’s an important difference in tests between the specification of what to test versus the implementation of how to test.

For example, my test specification might be “When the user enters their username and password and clicks the login button, then they are logged in and can see their recommendations”. This describes a scenario – it’s a specification of what the test should do. However, the test implementation has to deal with things like:

  • The username field is named “f_username”
  • The password field is named “f_password”
  • The login button is found via the CSS “#loginButton input”

If I change the layout of my login page, does the specification change? Hell no – I still need to provide credentials and click the login button. But has my implementation changed? Almost certainly!

Separating test specification from test implementation makes tests more robust. If I change how login works, I don’t want to have to change every single test that needs a logged in user – there’s probably a few of them! Instead, I only want to change the implementation – which fields to use, which buttons to press – in a single, common location.

Using Page Objects

In Selenium, you can separate specification from implementation by using page objects. That is, unlike in our previous example where the test code is littered with implementation details (how to find the keyword field or how to find the submit button), instead we move this logic into a page object.

If we built a page object to represent the Amazon home page, what would it look like?

public class AmazonHomePage {
    public static AmazonHomePage navigateTo(WebDriver driver);
    public AmazonSearchResultsPage searchFor(String searchTerm);
}

The homepage has two responsibilities that our test cares about:

  1. Given a browser, navigate to the page
  2. Once on the page, search for a specific search term

Notice that the searchFor operation returns a different type of page object – because after entering the search we will be on a new page: the search results page. What does that page look like?

public class AmazonSearchResultsPage {
    public String getTopResultTitle();
}

We only need one thing from our search results page – the title of the top result.

Tests with Page Objects

The page object encapsulates all the logic about how to perform certain actions. Our test now only needs to deal with what to actually do – so what does our test look like now?

AmazonHomePage homePage = AmazonHomePage.navigateTo(driver);
AmazonSearchResultsPage resultsPage =
    homePage.searchFor("iain banks");
assertThat(resultsPage.getTopResultTitle(), is("Transition"));

This is a pretty neat specification for our test: given a user on the amazon home page, when the user searches for ‘iain banks’, then the title of the top result is ‘Transition’.

There’s no implementation details in our test now. If any of the implementation details around searching change – we only need to change our page object. This is particularly important when the page objects are reused by multiple tests. Instead of having to manually change dozens of tests, we make our change in a single location – the page object.

Implementing Page Objects

By removing the implementation details from the test, they now sit within the page object. So how could we implement the page object interfaces we defined above? We could simply reuse the logic we had in our tests previously:

public AmazonSearchResultsPage searchFor(String searchTerm) {
    // Enter a search term
    WebElement keywordsField =
        driver.findElement(By.name("field-keywords"));
    keywordsField.sendKeys(searchTerm);

    // Click go
    WebElement goButton =
        driver.findElement(By.cssSelector("#navGoButton input"));
    goButton.click();
    ...

However, Selenium gives us another way to do this – we can also define the web elements declaratively, by using annotations. Although either approach is valid, using annotations tends to be neater. So let’s have a look at the top of the homepage class now:

public class AmazonHomePage {

	@FindBy(name="field-keywords")
	private WebElement keywordsField;

	@FindBy(css="#navGoButton input")
	private WebElement goButton;

Here we define the same two WebElements we had in our test before. But rather than call the web driver to find the elements for us explicitly, they’re now pre-populated based on the annotation.

To implement the interface we described above, we simply need to invoke the sendKeys and click methods on these elements as we did previously:

public AmazonSearchResultsPage searchFor(String searchTerm) {
    keywordsField.sendKeys(searchTerm);
    goButton.click();
    return PageFactory.initElements(driver,
        AmazonSearchResultsPage.class);
}

On lines two and three we type in our search term to the field we declared above and then click the go button. But what on earth is happening on lines four and five? This piece of magic – the PageFactory – is what allows us to use annotations to declare our elements.

Page Factory

The page factory instantiates our page object (AmazonSearchResultsPage) and finds the annotated fields. The fields are then initialised to the associated elements on the page. This is how we’re able to simply use the fields without having to initialise them. The returned search results page is then a fully populated page object, ready to be used by the test as before.

There’s one more method to see on the homepage – the navigateTo method. This also uses the page factory – this time to initialise our AmazonHomePage.

	public static AmazonHomePage navigateTo(WebDriver driver) {
		driver.get("http://www.amazon.co.uk/");
		return PageFactory.initElements(driver,
             AmazonHomePage.class);
	}

And there we have it – a better amazon search example – now using page objects and annotations. This is a much better way to structure tests than having implementation and specification intermingled within the test.

As before, all the examples are available on github. This article was part 2 in a series, if there’s a specific aspect of using Selenium you’d like to see covered in a future article let us know in the comments below.

Getting started with Selenium 2.0 (WebDriver)

Want to learn more about WebDriver? What do you want to know?

With the release of Selenium 2.0 the best name in web testing and the best API have come together in a match surely made in heaven. In this, the first article in a series, we’ll look at what it takes to start writing automated browser tests using Selenium 2.0.

What Is It?

Selenium is a browser-based testing tool. With it you can write tests that drive the browser – clicking on links, filling in forms – just like your users do; so you can test your application works correctly when used the way your users do. If you’re not doing browser-based end-to-end testing, how do you know your application works when your users actually use it?

What Do I Do?

  1. Download selenium or configure a maven dependency
  2. Write one or more tests
  3. Run them as part of your continuous integration
  4. Profit!

I’ll assume you know how to do (1) so we’ll start with (2) in this article. If you’re interested in (3), why not subscribe because we’ll cover that in future. (4) is an advanced topic and is left as an exercise for the reader.

All the examples below are on github – including an example maven pom if you’re so inclined.

Should I Use Selenium IDE?

NO. Unfortunately Selenium IDE isolates you from the mechanics of how your test is implemented. While this sounds like a good thing, it tends to lead to hard-to-maintain tests and a world of hurt. Instead, write your tests using your favourite testing framework – I’m working in Java, so the tests I’ll show below are JUnit tests.

My First Test

Let’s start with a really simple test, we’ll search Amazon for one of my favourite authors.

First, we need a driver – this is what actually interacts with a running browser – we’ll use the Firefox driver, you could instead use Chrome or Internet Explorer, if that’s what floats your boat.

FirefoxDriver driver = new FirefoxDriver();

This fires up a browser, ready to be controlled. Next we need to visit a page – so let’s go to the Amazon homepage.

driver.get("http://www.amazon.co.uk/");

Now we’re on the Amazon homepage, let’s enter in a search term.

WebElement keywordsField =
    driver.findElement(By.name("field-keywords"));
keywordsField.sendKeys("iain banks");

Wait a minute – what happened there? The first two lines tell the driver to find an element on the page. We’re identifying it by name, e.g. this would find something that looks like:

<input name="field-keywords" ... />

Once we’ve found that element on the page – we send it some keys. This simulates us typing in the search term into the relevant field.

But that’s not quite enough, we also need to execute the search. So let’s click the search button:

WebElement goButton =
    driver.findElement(By.cssSelector("#navGoButton input"));
goButton.click();

As before, we find an element – but this time we use a CSS selector. As the name suggests, this finds elements exactly the same way CSS selectors do. So this finds something that looks like:

    <div id="navGoButton">
        ...
          <input type="..." />
    </div>

Once we’ve found the element, we click on it – this loads the results page. Note: the call to click() will block until the page has finished loading, so whatever we do next will only happen once the page has loaded.

What do we do next? Let’s check that the top result is the book we expect it to be:

WebElement topResultTitle =
    driver.findElement(By.cssSelector("#result_0 .title a"));
assertThat(topResultTitle.getText(), is("Transition"));

Again we identify an element using a CSS selector, but this time we get the text of the element. This finds the relevant link and returns the text it contains – in this case, the title of the book.

If you run this test, you’ll see (racing by): the amazon home page open, the search term entered and the results page loaded.

Congratulations! We wrote our first Selenium test. Now let’s move on to part two: using page objects.