In this post, we will be learning why we need to use Base classes for handling different browsers and then will see an effecting way to share driver object across different files in side project.
Let’s understand why do we need to use this approach, what’s the issue with classic approach of having the driver initialisation in the test case itself.
Classic approach –
Basic selenium script to launch chrome browser and login to demo site –
import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.annotations.Test; import java.time.Duration; public class SampleSelenium { WebDriver driver = null; WebDriverWait wait; @Test public void MavenParamTest() throws InterruptedException { String browser = System.getProperty("browser", "chrome"); if(browser.contains("chrome")){ System.setProperty("webdriver.chrome.driver","/Users/skpatro/sel/chromedriver"); driver = new ChromeDriver(); }else if(browser.contains("firefox")){ System.setProperty("webdriver.gecko.driver","/Users/skpatro/sel/geckodriver"); driver = new FirefoxDriver(); } if(driver != null){ wait = new WebDriverWait(driver, Duration.ofSeconds(15)); driver.get("https://www.saucedemo.com/"); driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(20)); driver.findElement(By.id("user-name")).sendKeys("standard_user"); driver.findElement(By.id("password")).sendKeys("secret_sauce"); driver.findElement(By.id("login-button")).click(); Thread.sleep(2000); driver.quit(); } } }
This above script perfectly works when we run, but the issue is with the approach we write.
This is just one test case, so no issues, but when we keep adding test cases we can’t use the driver object as this is very local to the first test case.
Also when we use page object model in our framework, we might need the driver object across utility methods (let’s say to take screenshot) and also to each page classes.
Another issue is, if we re write the same lines of code to initialise the driver object in every test case, then this is code redundancy and maintenance will become huge.
Let’s capture this driver object creation and handling different browsers in a separate class so it can be shared in all files.
BaseTest.java – let’s place it in src/main/base package
package base; import io.github.bonigarcia.wdm.WebDriverManager; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; public class BaseTest { WebDriver driver = null; WebDriverWait wait; @BeforeClass public void setUp(){ String browser = System.getProperty("browser", "chrome"); if(browser.contains("chrome")){ //System.setProperty("webdriver.chrome.driver","/Users/skpatro/sel/chromedriver"); //WebDriverManager.chromedriver().browserVersion("92"); //WebDriverManager.chromedriver().driverVersion("93.0.4577.63"); WebDriverManager.chromedriver().setup(); driver = new ChromeDriver(); }else if(browser.contains("firefox")){ //System.setProperty("webdriver.gecko.driver","/Users/skpatro/sel/geckodriver"); WebDriverManager.firefoxdriver().setup(); driver = new FirefoxDriver(); }//keep adding for all other browsers PageDriver.setDriver(driver); } @AfterClass public void tearDown(){ PageDriver.getDriver().quit(); } }
I have kept the logic to instantiate the driver object inside setUp() and under @BeforeClass, so this can be triggered before each test class is running.
String browser = System.getProperty("browser", "chrome");
default is chrome browser, but you can change the value from command line as below –
mvn test -Dbrowser=firefox
PageDriver.setDriver(driver) -
Let’s create another class PageDriver.java which will be a getter and setter for driver object, so we can call this class to get the driver instance in all other files
setDriver() will accept the driver object from BaseTest.java
PageDriver.java
package base; import org.openqa.selenium.WebDriver; public class PageDriver { private static final ThreadLocal<WebDriver> webDriver = new ThreadLocal<>(); public static WebDriver getDriver(){ return webDriver.get(); } public static void setDriver(WebDriver driver){ webDriver.set(driver); } }
ThreadLocal<WebDriver> webDriver – is created to make the driver thread safe when running tests in parallel
setDriver() is a setter for the driver object
getDriver() is the getter
In any classes, we can call PageDriver.getDriver() to get the driver instance.
Now let’s implement the sample test case steps into a test case with above changes –
Test_Login.java
package testCases; import base.BaseTest; import base.PageDriver; import org.openqa.selenium.*; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.annotations.Test; import java.time.Duration; public class Test_Login extends BaseTest { @Test public void MavenParamTest() throws InterruptedException { WebDriver driver = PageDriver.getDriver(); driver.get("https://www.saucedemo.com/"); driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(20)); driver.findElement(By.id("user-name")).sendKeys("standard_user"); driver.findElement(By.id("password")).sendKeys("secret_sauce"); driver.findElement(By.id("login-button")).click(); Thread.sleep(2000); } }
As you can see the above test case, we have isolated the driver initialisation and no browser handling code, it’s all hidden behind the base package classes in src/main
Now you can add as many as test classes and extend your BaseTest class.
Where ever you need driver object, just call PageDriver.getDriver() and you are done!
Any modification to the BaseTest will be once and all other test cases can leverage the BaseTest.
Still there are some improvisations needed, the page objects like username, password fields are declared inside test cases which is not a good practice, we need to isolate them as well in page classes by implementing pageObjectModel.
TestCase should contain only test steps not the actual implementation.
Stay tuned and keep learning!
1 Comment