
Inheriting WebDriver Throughout Your Page Object Classes
I’ve seen (and written) several automation projects where each page class requires a constructor that takes a WebDriver object so that the page classes can access the current browser instance. There’s nothing particularly wrong with this, but I do find it a bit annoying to pass around the WebDriver like a hot potato. Instead, you can use inheritance.
My preferred approach is to create a base test class which defines a WebDriver object, instantiates it, and then sets it in a base page. Subsequent page classes then inherit from the base page, thus automatically having access to the driver.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package tests; import org.junit.AfterClass; import org.junit.BeforeClass; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import pages.Page; public class BaseTest { private static WebDriver webDriver; protected static Page basePage; private static final String APP_URL = "http://store.demoqa.com"; @BeforeClass public static void launchApplication(){ setChromeDriverProperty(); webDriver = new ChromeDriver(); webDriver.get(APP_URL); basePage = new Page(); basePage.setWebDriver(webDriver); } @AfterClass public static void closeBrowser(){ webDriver.quit(); } private static void setChromeDriverProperty(){ System.setProperty("webdriver.chrome.driver", "resources/chromedriver"); } } |
Notice that here in the base test class, in addition to instantiating the WebDriver, we also create a handle to the base page and set the WebDriver in that page. By doing this, no tests need to touch the WebDriver.
Here’s an example of the Page class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package pages; import org.openqa.selenium.By; import org.openqa.selenium.Keys; import org.openqa.selenium.WebDriver; public class Page { protected static WebDriver driver; private static final String SEARCH_FIELD_CLASS = "s"; private static final String CART_LINK_CLASS = "cart_icon"; public void setWebDriver(WebDriver driver) { Page.driver = driver; } public void search(String query) { driver.findElement(By.className(SEARCH_FIELD_CLASS)).sendKeys(query + Keys.ENTER); } public CartPage clickCheckoutLink() { driver.findElement(By.className(CART_LINK_CLASS)).click(); return new CartPage(); } } |
I use the Page class as a base that contains functionality that is available on any page within the application, such as the search and clickCheckoutLink functions. Then any new page object class I create, can simply inherit from this class, thus gaining access to the WebDriver. Notice there’s no need for a constructor that accepts the WebDriver object in this page class.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package pages; import org.openqa.selenium.By; public class CartPage extends Page { private static final String PRODUCT_TABLE = "//table[@class='checkout_cart']"; private static final String PRODUCT_TABLE_ROWS = PRODUCT_TABLE + "/tbody/tr[contains(@class, 'product_row')]"; public int getNumberOfProductsInCart() { return driver.findElements(By.xpath(PRODUCT_TABLE_ROWS)).size(); } } |
Here’s an example of a simple test just to make sure everything works. Test classes that extend from BaseTest will not need to instantiate a page class to get started, as they inherit the basePage instance from BaseTest.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package tests; import org.junit.Test; import org.junit.Assert; import pages.CartPage; public class CartTests extends BaseTest { @Test public void testEmptyCart() { CartPage cart = basePage.clickCheckoutLink(); Assert.assertEquals("Cart should be empty", 0, cart.getNumberOfProductsInCart()); } } |
NON-STATIC WEBDRIVER
I received a question on Twitter asking is there a way to do this without making the WebDriver static.
@techgirl1908 Hi, how do you make webdriver as non static and make it available throughout the project. Can you point me to framework or project where webdriver is made non- static.
— @sreeaga (@sreeagasth) December 11, 2017
If using Junit which comes with the Selenium dependencies, their Before and After methods are static and therefore force some things upon us. We can still get around making the WebDriver static but it takes a bit of wizardry. You can essentially make a static instance to the BaseTest class itself, and create a non-default constructor that does what your static setup method would have.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
package tests; import org.junit.AfterClass; import org.junit.BeforeClass; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import pages.Page; public class BaseTest { private WebDriver webDriver; private static BaseTest baseTest; protected static Page basePage; private final String APP_URL = "http://store.demoqa.com"; public BaseTest() { this(false); } public BaseTest(boolean launch) { if(launch) { setChromeDriverProperty(); webDriver = new ChromeDriver(); webDriver.get(APP_URL); basePage = new Page(); basePage.setWebDriver(webDriver); } } @BeforeClass public static void launchApplication(){ baseTest = new BaseTest(true); } @AfterClass public static void closeBrowser(){ baseTest.webDriver.close(); } private static void setChromeDriverProperty(){ System.setProperty("webdriver.chrome.driver", "resources/chromedriver"); } } |
All other classes remain the same. This works, but feels hacky to me, which is why I prefer the first approach. But I understand people’s projects may require different things. Another alternative would be to use TestNG or another assertion tool that does not require static Before and After methods.
Timothy Western
This is similar to am approach I’ve used several times. It allows a lot of setup to happen consistently through the parent constructor. And it does so while allowing the possibility of extension not for social cases without to much trouble.
Samreen Chughtai
How can I implement it in PageFactory. Where to initiate page object elements??
Eliezer
protected static WebDriver driver;
}
Sivasankaramalan
If you want to use (
WebDriverWait wait = new WebDriverWait( driver , 30);) in different class file how we inheriting the WebDriver??
Bi Chand
Can u please do similar example with PageFactory?
Eliezer
protected static WebDriver driver;
}
Zoé Thivet
Used it in my new project. Thanks for this tip
Madhuri R Gaddam
Please can you share how to use this using Testng.
Angie Jones
Madhuri R Gaddam
Angie , I appreciate your quick response I am able to implement this.
Ifeoma Emodi
Am not sure if this is the right question but in the BaseTest class above, you did add
Angie Jones
No, I only do it for Page which is a super class for all other pages.
Owyn Dwight
Hey Angie,
Angie Jones
Yes, the Selenium project lead, Simon Stewart advises against using it and has said it was a mistake to add it to the project. It’s even been deprecated in some of the languages.
https://sqa.stackexchange.com/questions/37553/pagefactory-is-deprecated-in-c-net-what-to-use-instead-of-that
Rahul
Please send the demo project for same approach which you used.