Question

Quick question about page objects in selenium webdriver. our site is very dynamic with lots of ajax and various authentication states. It is tough to figure out how to define each page object BUT lets say I have figured that out and defined several page objects that represent our site.

How do you handle crossing from page to page. So I get a page object for my home page and one for my account page and one for my results page. Then I need to write a test that traverses all my pages to simulate a user performing multiple actions.

How do you say give me a HomePage object to create a new use -> then get a account page object to go perform some user actions - then get a result page object to verify those actions all from a single script.

How are people doing this?

thanks

Was it helpful?

Solution

When you're simulating having the user enter a new URL into the URL bar of the browser, then it's the responsibility of the test class to create the page object it needs.

On the other hand, when you're doing some operation on the page that would cause the browser to point to another page -- for example, clicking a link or submitting a form -- then it's the responsibility of that page object to return the next page object.

Since I don't know enough about the relationships between your home page, account page, and result page to tell you exactly how it'd play out in your site, I'll use an online store app as an example instead.

Let's say you've got a SearchPage. When you submit the form on the SearchPage, it returns a ResultsPage. And when you click on a result, you get a ProductPage. So the classes would look something like this (abbreviated to just the relevant methods):

public class SearchPage {

    public void open() {
        return driver.get(url);
    }

    public ResultsPage search(String term) {
        // Code to enter the term into the search box goes here
        // Code to click the submit button goes here
        return new ResultsPage();
    }

}

public class ResultsPage {

    public ProductPage openResult(int resultNumber) {
        // Code to locate the relevant result link and click on it
        return new ProductPage();
    }

}

The test method to execute this story would look something like this:

@Test
public void testSearch() {

    // Here we want to simulate the user going to the search page
    // as if opening a browser and entering the URL in the address bar. 
    // So we instantiate it here in the test code.

    SearchPage searchPage = new SearchPage();
    searchPage.open(); // calls driver.get() on the correct URL

    // Now search for "video games"

    ResultsPage videoGameResultsPage = searchPage.search("video games");

    // Now open the first result

    ProductPage firstProductPage = videoGameResultsPage.openResult(0);

    // Some assertion would probably go here

}

So as you can see, there's this "chaining" of Page Objects where each one returns the next one.

The result is that you end up with lots of different page objects instantiating other page objects. So if you've got a site of any considerable size, you could consider using a dependency injection framework for creating those page objects.

OTHER TIPS

Well, I created my own Java classes which represent the pages:

Say, the below is code to represent home page. Here user can login:

public class HomePage{
  private WebDriver driver;
  private WebElement loginInput;
  private WebElement passwordInput;
  private WebElement loginSubmit;

  public WebDriver getDriver(){
    return driver;
  }

  public HomePage(){
    driver = new FirefoxDriver();
   }

  public CustomerPage login(String username, String password){
     driver.get("http://the-test-page.com");
     loginInput = driver.findElement(By.id("username"));
     loginInput.sendKeys(username);
     passwordInput = driver.findElement(By.id("password"));
     passwordInput.sendKeys(password);
     loginSubmit = driver.findElement(By.id("login"));
     loginSubmit.click();
     return new CustomerPage(this);
  }


}

And the page for Customer can look like this. Here I am demonstrating, how to get, say, logged in user:

public class CustomerPage{
    private HomePage homePage;
    private WebElement loggedInUserSpan;

 public CustomerPage(HomePage hp){
    this.homePage = hp;
  }

 public String getLoggedInUser(){
      loggedInUserSpan = homePage.getDriver().findElement(By.id("usrLongName"));
      return loggedInUserSpan.getText();
 }

}

And the test can go like this:

@Test
public void testLogin(){
  HomePage home = new HomePage();
  CustomerPage customer = home.login("janipav", "extrasecretpassword");
  Assert.assertEquals(customer.getLoggedInUser(), "Pavel Janicek");
}

You generally want to model what a user actually does when using your site. This ends up taking the form of a Domain Specific Language (DSL) when using page objects. It gets confusing with reusable page components though.

Now that Java 8 is out with default methods, reusable page components can be treated as mixins using default methods. I have a blog post with some code samples found here that explains this in more detail: http://blog.jsdevel.me/2015/04/pageobjects-done-right-in-java-8.html

I suggest you use a framework that provides support for these patterns. Geb is one of the best one out there. Below is an example taken from their manual

Browser.drive {
    to LoginPage
    assert at(LoginPage)
    loginForm.with {
        username = "admin"
        password = "password"
    }
    loginButton.click()
    assert at(AdminPage)
}

class LoginPage extends Page {
    static url = "http://myapp.com/login"
    static at = { heading.text() == "Please Login" }
    static content = {
        heading { $("h1") }
        loginForm { $("form.login") }
        loginButton(to: AdminPage) { loginForm.login() }
    }
}

class AdminPage extends Page {
    static at = { heading.text() == "Admin Section" }
    static content = {
        heading { $("h1") }
    }
}

I enjoy writing Selenium Webdriver tests using the Page Object pattern. But was personally annoyed at the verbosity and repetition of having to always explicitly instantiate and return the next page or page component. So with the benefit of Python's metaclasses I wrote a library, called Keteparaha, that automatically figures out what should be returned from a selenium page object's method calls.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top