So far, we explored a very simple Page Object implementation for a single page web application. We can use the Page Object model to implement the objects of a page in a complex web application to simplify the testing.
In this recipe, we will create the objects of the Page Object model for the search functionality in an e-commerce application found at http://demo.magentocommerce.com/. We can implement a Page Object model even if the specific functionality is not a page of its own.
Each page of the application provides the user with the ability to search for products on the site by entering a query and hitting the search button. When a search query is submitted, the application returns a new page with a list of products matching the search query. Using the approach described in this recipe, we can build a more modular framework as shown in following diagram:
In this recipe, we will create this nested hierarchy, where the HomePage
class implements the Search
class, which in turn returns an object of the SearchResults
class to the test.
Identify the page and logical relationship between pages to build a nested component set.
To implement nested Page Object instances, perform the following steps:
Browser
class, which will provide a static and shared WebDriver instance for all the pages, as follows:package com.secookbook.examples.chapter08.pageobjects; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; public class Browser { private static WebDriver driver = new ChromeDriver(); public static WebDriver driver() { return driver; } public static void open(String url) { driver.get(url); } public static void close() { driver.quit(); } }
HomePage
class, which allows the test to navigate to the home page of the application. It also provides access to the Search
class, which is shared among the various pages of the application, as follows:package com.secookbook.examples.chapter08.pageobjects; import org.openqa.selenium.WebDriver; import org.openqa.selenium.support.PageFactory; import org.openqa.selenium.support.ui.LoadableComponent; import static org.junit.Assert.*; public class HomePage extends LoadableComponent<HomePage> { private WebDriver driver; static String url = "http://demo.magentocommerce.com/"; private static String title = "Madison Island"; public HomePage(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); } @Override public void load() { Browser.open(url); } @Override public void isLoaded() { assertEquals(title, driver.getTitle()); } public Search Search() { Search search = new Search(driver); return search; } }
Search
class. This class provides all the objects of the Page Object model to search for products in the application, as follows:package com.secookbook.examples.chapter08.pageobjects; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class Search { private WebDriver driver; private WebElement search; @FindBy(css = "button.button") private WebElement searchButton; public Search(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); } public SearchResults searchInStore(String query) { search.sendKeys(query); searchButton.click(); return new SearchResults(driver, query); } }
SearchResult
class, which is nested in the Search
class. The SearchResult
class represents the results page when the user submits a search query, as discussed previously. The SearchResult
class also provides access to the Search
class so that users can search again for a different query. This is done as follows:package com.secookbook.examples.chapter08.pageobjects; import java.util.ArrayList; import java.util.List; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.PageFactory; import org.openqa.selenium.support.ui.LoadableComponent; import static org.junit.Assert.*; public class SearchResults extends LoadableComponent<SearchResults> { private WebDriver driver; private String query; public SearchResults(WebDriver driver, String query) { this.driver = driver; PageFactory.initElements(driver, this); } @Override public void isLoaded() { assertEquals("Search results for: '" + this.query + "'", driver.getTitle()); } public List<String> getProducts() { List<String> products = new ArrayList<String>(); List<WebElement> productList = driver.findElements( By.cssSelector("ul.products-grid > li")); for (WebElement item : productList) { products.add(item.findElement(By.cssSelector("h2 > a")).getText()); } return products; } public Search Search() { Search search = new Search(driver); return search; } @Override protected void load() { // TODO Auto-generated method stub } }
package com.secookbook.examples.chapter08.tests; import org.junit.Test; import com.secookbook.examples.chapter08.pageobjects.Browser; import com.secookbook.examples.chapter08.pageobjects.HomePage; import com.secookbook.examples.chapter08.pageobjects.SearchResults; import static org.junit.Assert.*; public class SearchTest { @Test public void testProductSearch() { try { // Create an instance of Home page HomePage homePage = new HomePage(Browser.driver()); // Navigate to the Home page homePage.get(); // Search for 'phones', the searchInStore method will return // SerchResults SearchResults searchResult = homePage.Search().searchInStore( "phones"); // Verify there are 2 products available with this search assertEquals(2, searchResult.getProducts().size()); assertTrue(searchResult.getProducts().contains( "MADISON OVEREAR HEADPHONES")); } finally { Browser.close(); } } }
The test starts by creating an instance of the HomePage
class. As you can see, the application home page provides the user with the ability to search the site. In a similar way, the HomePage
class provides the ability to search by using the Search
class, as follows:
HomePage homePage = new HomePage(Browser.driver()); homePage.get(); SearchResults searchResult = homePage.Search().searchInStore("phones");
The searchInStore()
method of the Search
class takes the query string as arguments and interacts with the search form to send the query. It returns the SearchResults
class as a page. The SearchResults
class provides a list of products that match the query back to the test using the getProducts()
method, as follows:
public List<String> getProducts() { List<String> products = new ArrayList<String>(); List<WebElement> productList = driver.findElements( By.cssSelector("ul.products-grid > li")); for (WebElement item : productList) { products.add(item.findElement(By.cssSelector("h2 > a")).getText()); } return products; }
The test can verify the number of products as well as the names of the products by checking the return value of the getProducts()
method, as follows:
assertEquals(2, searchResult.getProducts().size()); assertTrue(searchResult.getProducts().contains( "MADISON OVEREAR HEADPHONES"));
Using the nested objects of the Page Object model, we can build a logical chain of pages within an application and build a robust and maintainable test automation framework.
3.144.35.122