Some Selenium projects use static page methods in all page classes instead of creating page objects.
For example, the following HomePage class uses only static methods:
public class HomePage {
private static final String HOME_PAGE_URL = "https://www.vpl.ca/";
private static final By SEARCH_BOX_ID = By.id("edit-keyword");
private static final By SEARCH_BUTTON_ID = By.id("edit-submit");
public static void open(WebDriver d) {
d.get(HOME_PAGE_URL);
}
public static boolean isDisplayed(WebDriver d) {
return getUrl(d).equalsIgnoreCase(HOME_PAGE_URL);
}
public static String getUrl(WebDriver d) {
return d.getCurrentUrl();
}
public static void searchBy(String keyword, WebDriver d) {
typeKeyword(keyword, d);
clickSearchButton(d);
}
public static void typeKeyword(String keyword, WebDriver d) {
WebElement searchBox = d.findElement(SEARCH_BOX_ID);
searchBox.sendKeys(keyword);
}
public static void clickSearchButton(WebDriver d) {
WebElement searchButton = d.findElement(SEARCH_BUTTON_ID);
searchButton.click();
}
}
ResultsPage class uses only static methods as well:
public class ResultsPage {
private static final String RESULTS_PAGE_URL =
"https://vpl.bibliocommons.com";
private static final By RESULTS_FOUND_XPATH =
By.xpath("(//span[@data-key = 'pagination-text'])[1]");
private static String pageXpath =
"//li[contains(@class, 'page-number')]/a[@data-page = '%s']";
public static String getUrl(WebDriver d) {
return d.getCurrentUrl();
}
public static boolean isDisplayed(WebDriver d) {
return getUrl(d).startsWith(RESULTS_PAGE_URL);
}
public static String getResultsFound(WebDriver d) {
WebElement label = d.findElement(RESULTS_FOUND_XPATH);
return label.getText();
}
public static void goToPage(int n, WebDriver d)
throws InterruptedException {
scrollToBottom(d);
By xpath = getPageXpath(n);
Thread.sleep(2000);
WebElement page = d.findElement(xpath);
page.click();
}
private static By getPageXpath(int n) {
String locator = String.format(pageXpath, n);
return By.xpath(locator);
}
public static void scrollToBottom(WebDriver d) {
((JavascriptExecutor) d).executeScript(
"window.scrollTo(0, document.body.scrollHeight)");
}
public static boolean urlContains(int pageNumber, WebDriver d) {
return d.getCurrentUrl().contains("page=" + pageNumber);
}
}
Because of the static methods, the test is simpler since there are no page objects created in it:
@Test
public void canNavigateToOtherPagesTest() throws InterruptedException {
HomePage.open(driver);
Verify.verify(HomePage.isDisplayed(driver));
HomePage.searchBy(KEYWORD, driver);
Verify.verify(ResultsPage.isDisplayed(driver));
Verify.verify(ResultsPage.getResultsFound(driver).contains("1 to 10"));
ResultsPage.goToPage(2, driver);
Assert.assertTrue(ResultsPage.urlContains(2, driver));
ResultsPage.goToPage(3, driver);
Assert.assertTrue(ResultsPage.urlContains(3, driver));
}
This code is indeed simpler when no page objects are created in it.
It is also not optimal.
Notice how all static methods need the driver as a parameter.
This is a bad case of code duplication which we should not allow.
The second problem is that there is no clear explanation that the user moves from the Home Page to Results Page after searching in the Home Page. Since both page classes prefer static page methods to page objects, it is not possible to return a Results Page class from the searchBy() method.
What is the solution?
Do not use static page methods.
Create the page object in a constructor, add one parameter to the constructor for the driver, set the driver class field in the constructor.
This is the same Home Page without static methods:
public class HomePage {
private WebDriver driver;
private static final String HOME_PAGE_URL = "https://www.vpl.ca/";
private static final By SEARCH_BOX_ID = By.id("edit-keyword");
private static final By SEARCH_BUTTON_ID = By.id("edit-submit");
public HomePage(WebDriver driver) {
this.driver = driver;
}
public void open() {
driver.get(HOME_PAGE_URL);
}
public boolean isDisplayed() {
return getUrl().equalsIgnoreCase(HOME_PAGE_URL);
}
public String getUrl() {
return driver.getCurrentUrl();
}
public ResultsPage searchBy(String keyword) {
typeKeyword(keyword);
clickSearchButton();
return new ResultsPage(driver);
}
public void typeKeyword(String keyword) {
WebElement searchBox = driver.findElement(SEARCH_BOX_ID);
searchBox.sendKeys(keyword);
}
public void clickSearchButton() {
WebElement searchButton = driver.findElement(SEARCH_BUTTON_ID);
searchButton.click();
}
}
Also, ResultsPage class without static methods:
public class ResultsPage {
private WebDriver driver;
private static final String RESULTS_PAGE_URL =
"https://vpl.bibliocommons.com";
private static final By RESULTS_FOUND_XPATH =
By.xpath("(//span[@data-key = 'pagination-text'])[1]");
private static String pageXpath =
"//li[contains(@class, 'page-number')]/a[@data-page = '%s']";
public ResultsPage(WebDriver driver) {
this.driver = driver;
}
public String getUrl() {
return driver.getCurrentUrl();
}
public boolean isDisplayed() {
return getUrl().startsWith(RESULTS_PAGE_URL);
}
public String getResultsFound() {
WebElement label = driver.findElement(RESULTS_FOUND_XPATH);
return label.getText();
}
public void goToPage(int n) throws InterruptedException {
scrollToBottom();
By xpath = getPageXpath(n);
Thread.sleep(2000);
WebElement page = driver.findElement(xpath);
page.click();
}
public void scrollToBottom() {
((JavascriptExecutor) driver).executeScript(
"window.scrollTo(0, document.body.scrollHeight)");
}
public boolean urlContains(int pageNumber) {
return driver.getCurrentUrl().contains("page=" + pageNumber);
}
private By getPageXpath(int n) {
String locator = String.format(pageXpath, n);
return By.xpath(locator);
}
}
The test method with no static page methods looks like this:
@Test
public void canNavigateToOtherPagesTest() throws InterruptedException {
HomePage homePage = new HomePage(driver);
homePage.open();
Verify.verify(homePage.isDisplayed());
ResultsPage resultsPage = homePage.searchBy(KEYWORD);
Verify.verify(resultsPage.isDisplayed());
Verify.verify(resultsPage.getResultsFound().contains("1 to 10"));
resultsPage.goToPage(2);
Assert.assertTrue(resultsPage.urlContains(2));
resultsPage.goToPage(3);
Assert.assertTrue(resultsPage.urlContains(3));
}
This version of the test method and of the page classes is much better than the one with the static methods because:
only 1 page object is created in the test (homePage)
it is clear how the user navigates from home page to results page; results page is returned by the searchBy() method
there is no code duplication; the driver is only passed to the contructor of the HomePage class in the test method