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.

35 thoughts on “Page Objects in Selenium 2.0

  1. Terrific series of posts! Clear easy-to-follow explanations are what we need to use the new selenium/web driver release.

    Any link to suggest on how to setup the whole selenium server / grid 2?

  2. kilapartirajival

    I’m trying to navigate to a particular iframe, and I could not get the whole source of the web app, as the view source is blocked, and i could find the source only for this particular iframe. Can you please advise me how to go about navigating to this particular iframe, and get hold of the elements in it ? Because, the elements in that iframe are not invisible to webdriver, and it keeps on giving messages that the specified element does not exist.

  3. Vishal

    I’m calling switchTo(“frame”), and then trying to find the element in that iFrame. But the execute method in the webdriver is passing me a Status Code “7”: which means “NoSuchElement An element could not be located on the page using the given search parameters.” (See: http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes). But my element is clearly there in that iFrame. Can you pls suggest any debugging techniques, so that I can move forward ?

  4. Vishal

    Oops, I’m really sorry. There was another iFrame hiding in this iFrame. So I switched to that iFrame after calling the first switchTo(“frame”). Now everything seems working … 🙂

  5. Vishal

    Now, the problem is that I’m able to find the element, but I’m not able to expand it (or click it, so that it will expand). Below is the html tag:

    Manage Info

    Any workaround for this please ?

    1. Hi Vishal – glad you got the original problem sorted. Not sure why clicking on it wouldn’t work. Are you getting an error when you call click()? There’s probably something specific about your case, I’m afraid. Without seeing much more detail I can’t really guess as to what might be going wrong.

  6. Vishal

    I’m not getting any error when i do a click. I modified the click() method to give me a return code, and it is 0, which says sucess. But however it is not clicking the span id. In my above post I’ve included the html tag, but I think this site is not allowing to post html tag content. Below is the content before clicking the element:

    (lt than)span id=”manage” class=”undefined” type=”label” xmlsrc=”” align=”left” title=”ManageInfo”(gt than)Manage Info(lt than)/span(gt than)
    (lt than)div class=”hide” type=”container”(gt than)

    After clicking the span id element, below is the tag

    (lt than)span id=”manage” class=”clsCurrentHasFocus” type=”label” xmlsrc=”” align=”left” title=”ManageInfo”(gt than)Manage Info(lt than)/span(gt than)
    (lt than)div class=”shown” type=”container”(gt than)

    Pls see the class=”clsCurrentHasFocus” and div class = “shown” after clicking the element.

    1. Is there some javascript firing when you click the span? I don’t see an onclick or anything. Whatever is updating the css class obviously isn’t being triggered by webdriver, so it depends what actually triggers it, because it doesn’t look like its the element itself.

  7. KV

    One question , in the following code snippet , from where do we get reference to “driver” parameter passed. “driver” is not declared at class level

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

    I tried to replicate and my IDE is showing compile time error

  8. Cat

    Are you familiar with any design patterns that would help in, say, a few pages with the same header/footer that you’d like to interact with? Say the Sign-in/out ability is on every page throughout the session as a header. I want to be able to sign out of my session from any of those pages. I’ve thought about creating a generic page with the header and footer elements only and extend (Java) from there but I’m not sure the page elements can be instantiated that way. Any thoughts are welcome!

    1. Hi Cat,

      I think I’ve used inheritance for this situation in the past. I can’t remember offhand whether the annotations work in derived classes – but you’d certainly hope so. One to try as it definitely sounds like the right approach.

      Even if the annotations don’t work, accessing the page elements in code in derived classes is still worth the benefit of having a single place where common page elements are defined.

  9. Frank

    Hi Dave

    Thanks for that great article.
    But still got a question: Is it possible to tell the PageFactory not to tell the std constructor but a handmade one (including parameters).
    Do you have a workaround?
    I would appreciate it if you could give me a hint

    1. Hi Frank,
      It looks like you can use PageFactory.initElements(…) to sort out the annotated fields in an already constructed object. So for example, you could manually construct your page object with some complex constructor call, then call initElements(…) to setup the fields.

  10. pawan

    HI Dave,
    Very Nice description, cleared my doubts ….But i have one question ..

    Can we also place all the web-elements in some another separate file, what do you say is it better to place all the elements on some separate file like property file. Or it is better be place in the Page object.

  11. Jey

    Hi return PageFactory.initElements(driver,…) driver still referring the login url i.e., has the same reference of the login page. How the Home page reference page will updated in driver?

    1. Hi, PageFactory.initElements only creates a Page object of the required type, with it’s fields initialised. The goButton.click() call actually moves the browser to a new page. So first you execute an action that moves to a new page, then you create a page object to allow your tests to interact with the new page.

  12. Umesh

    This is very nice presentation on PageObject. I am new to PageObject so I am bit confused on a decision.

    I can create a separate class for my Test Implementations and another UI form consisting my tests which calls these separate Test Implementations classes.

    Similarly to your example, I am passing WebDriver object to these Test Implementations classes and I am able to perform my test logic so Why I need to use PageObjects?

    1. Your “Test Implementation” sounds very similar to the idea of a page object. The idea is to separate how to drive the page from what, logically, the test wants to do. E.g. the difference between how to locate the username field in the HTML, vs what you want to see in the test “login with username X”. Page objects are one just pattern for doing that.

  13. Shruthi

    Hi David,
    Very nice explanation. I had one question regarding PageFactory web element initialization. How should we select the web elements that needs to be initialized? For example, my page might have many page elements out of which I might just use a few at any point or instance. So should I just initialize the main web elements in the page and Lazy initialize i.e find element as and when required?

    1. I don’t see why you wouldn’t normally initialise all the page elements in one go, unless you’ve got hundreds – in which case you’ve got other problems! There are cases where page elements aren’t available all at the same time (e.g. a dynamic, javascript heavy site), in which case you have to be more selective about what to initialise when.

  14. Arun

    HI David,

    Thanks for the post. Can we data drive the page object design pattern? I tried Hybrid frame work with page objects. It work fine for single set of data and the same fails for group of data.

    My framework is little different. All my test statements are in one excel file and test data in another excel file. Can you give me any mail id (I do not have any of the 3 accounts given below)so that i can send the sample project,

    1. Honestly, I wouldn’t use the excel-driven approach. I’ve seen it done and its not great, the details of *what* you’re testing are completely obscured. Hire developers, let them write tests. If you have a QA team that only want to work in Excel, fire them.

  15. Bhargavi

    Just came across your blog while i was looking for some notes on Page objects .You’ve made it looks simple and easy to understand !! Thanks !! 😀

    1. Hmm, interesting. I’m not sure I’d agree with using inheritance (always) in this case. Especially when you have different types of pages and different sets of common elements: it might be more flexible to favour composition over inheritance.

      1. Thanks, David. Point taken. In my blog post I mentioned other strategies for Ruby and Python, and should have preferred a different one even for C#. I’ve revised my code already, and will make some sort of change in my blog post.

  16. Karn

    Thank you for explaining the PageObject concept with so much elan. I must say this is one of the most useful tutorials I have stumbled upon for Page Objects. Keep sharing the knowledge 🙂

  17. Joce

    I’m new to Selenium. I downloaded your code from GitHub, and imported them into IntelliJ. Could you please tell me how to run it? Not seeing Run….context menu by right-click @Test method. Thanks.

Leave a reply to David Green Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.