0

I am trying to get the driver that was set in the ThreadLocal in the "BaseTest" class to my Test class "T001_LoginTests_Check" while creating an object of my Page class "HomePage_Check". I am passing the driver through the constructor defined in the Page class.

This is what I have done so far and issue I am facing as follows:

I have created a thread safe driver in my "BaseTest" class. Whenever I am going to get the driver stored in the ThreadLocal in my Test class that inherits the "BaseTest" class, I get a NullPointerException.

My "BaseTest" class is as follows:

package base;

import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.remote.AbstractDriverOptions;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;

import java.io.FileInputStream;
import java.util.Properties;

public class BaseTest {

    public WebDriver driver;
    Properties prop;

    public BaseTest(){
        loadProperties();
    }

    public static final ThreadLocal<WebDriver> tsDriver = new ThreadLocal<>();

    private void setDriver(WebDriver driver){
        tsDriver.set(driver);
    }

    public static synchronized WebDriver getDriver(){

        return tsDriver.get();
    }

    public WebDriver getBrowser(){

        AbstractDriverOptions absOptions;

        String browser = prop.getProperty("browser");

        if (browser.equalsIgnoreCase("Chrome")){
            absOptions = new ChromeOptions();
            driver = WebDriverManager.chromedriver().capabilities(absOptions).create();
        } else if (browser.equalsIgnoreCase("Firefox")) {
            absOptions = new FirefoxOptions();
            driver = WebDriverManager.firefoxdriver().capabilities(absOptions).create();
        } else if (browser.equalsIgnoreCase("Edge")) {
            absOptions = new EdgeOptions();
            driver = WebDriverManager.edgedriver().capabilities(absOptions).create();
        } else {
            System.out.println("[i] Invalid argument! Spawning Chrome Driver as default.");
            absOptions = new ChromeOptions();
            driver = WebDriverManager.chromedriver().capabilities(absOptions).create();
        }

        return driver;

    }

    public void loadProperties() {

        prop = new Properties();

        try{
            FileInputStream fis = new FileInputStream("D:\\SQA\\webdriver_java\\rokomari.com_automation" +
                    "\\src\\test\\java\\config\\config.properties");
            prop.load(fis);
        }catch (Exception e){
            System.out.println("[!] Some unexpected error occurred during loading the properties file.");
        }

    }

    @BeforeSuite
    public void setUp(){
        driver = getBrowser();
        driver.get(prop.getProperty("url"));
        driver.manage().window().maximize();

        setDriver(driver);

    }

    @AfterSuite
    public void cleanUp(){
        getDriver().quit();
    }
}

And my Test class is as follows. Please note that I'm passing the driver to the page class while creating an object for my Page class and trying to receive the driver by the Page class constructor.

package tests;

import base.BaseTest;
import org.openqa.selenium.WebDriver;
import org.testng.annotations.Test;
import pages.HomePage_Check;

public class T001_LoginTests_Check extends BaseTest {

    HomePage_Check homePageCheck = new HomePage_Check(getDriver());

    @Test
    public void pageTitleTest(){

        System.out.println("[?] Called from test class -> " + driver);
        homePageCheck.getPageTitle();
    }

}

I am also putting my Page class below for better understanding:

package pages;

import org.openqa.selenium.WebDriver;

public class HomePage_Check {

    public WebDriver driver;

    public HomePage_Check(WebDriver driver){
        this.driver = driver;
    }

    public void getPageTitle(){

        System.out.println(driver.getTitle());
        
    }

}

My config.properties file contains following data:

browser = Chrome url = https://www.rokomari.com/book

My code is generating the following error:

java.lang.NullPointerException: Cannot invoke "org.openqa.selenium.WebDriver.getTitle()" because "this.driver" is null

    at pages.HomePage_Check.getPageTitle(HomePage_Check.java:19)
    at tests.T001_LoginTests_Check.pageTitleTest(T001_LoginTests_Check.java:14)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
    at java.base/java.lang.reflect.Method.invoke(Method.java:578)
    at org.testng.internal.invokers.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:139)
    at org.testng.internal.invokers.TestInvoker.invokeMethod(TestInvoker.java:677)
    at org.testng.internal.invokers.TestInvoker.invokeTestMethod(TestInvoker.java:221)
    at org.testng.internal.invokers.MethodRunner.runInSequence(MethodRunner.java:50)
    at org.testng.internal.invokers.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:969)
    at org.testng.internal.invokers.TestInvoker.invokeTestMethods(TestInvoker.java:194)
    at org.testng.internal.invokers.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:148)
    at org.testng.internal.invokers.TestMethodWorker.run(TestMethodWorker.java:128)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at org.testng.TestRunner.privateRun(TestRunner.java:829)
    at org.testng.TestRunner.run(TestRunner.java:602)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:437)
    at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:431)
    at org.testng.SuiteRunner.privateRun(SuiteRunner.java:391)
    at org.testng.SuiteRunner.run(SuiteRunner.java:330)
    at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
    at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:95)
    at org.testng.TestNG.runSuitesSequentially(TestNG.java:1256)
    at org.testng.TestNG.runSuitesLocally(TestNG.java:1176)
    at org.testng.TestNG.runSuites(TestNG.java:1099)
    at org.testng.TestNG.run(TestNG.java:1067)
    at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
    at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:105)

All I want is to pass the driver from ThreadLocal through my Test class while creating an object of my Page class and the driver will be received by the Page class constructor. That's it.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • Why have a ThreadLocal when you are passing the value around as method parameter? But I suspect `@BeforeSuite` is executed in a different thread than your tests => the driver of other threads is empty (because it is only set for the setup thread) – knittl May 08 '23 at 09:52
  • 1
    If your driver is thread-safe, why are you using a ThreadLocal? In addition, why are you using `synchronized` to access the ThreadLocal? If this produces a null for you, then likely the setup was done on a different thread than the actual test is running. – Mark Rotteveel May 08 '23 at 09:53
  • The driver I have set in the ThreadLocal in setUp() method in the BaseTest Class is the driver I want when I'm passing the driver in the Test Class (T001_LoginTests_Check). And that same driver should be in the Page Class (HomePage_Check) via constructor. But whenever I'm calling the driver in the ThreadLocal form my Test Class or anywhere outside the BaseTest it's being null. What should I do? – Aliul Islam Abir May 08 '23 at 10:01
  • Why are you using a `ThreadLocal`? Do you need distinct drivers for distinct threads? Why can't you store a plain reference and then get the reference? – knittl May 08 '23 at 12:42
  • Yes, I need a distinct driver to be used in all of my test cases. And I need a thread safe driver to run my test cases in parallel. The driver that spawns the browser is stored in the ThreadLocal and I need the very same driver in my Test Classes and Page Classes as well. But whenever I try to call the driver from ThreadLocal outside the BaseTest Class it becomes null. – Aliul Islam Abir May 08 '23 at 12:50
  • @AliulIslamAbir but why do you need a `ThreadLocal` for that? Assuming that you really really need to, consider `ThreadLocal.withInitial` to set up your driver instance each time a thread requests the driver for the first time. – knittl May 08 '23 at 13:07
  • Can you show me a sample code please. It will be a great help. – Aliul Islam Abir May 08 '23 at 13:35

1 Answers1

1

Try this below code approach and I hope it clears your issue.

package tests;

import base.BaseTest;
import org.openqa.selenium.WebDriver;
import org.testng.annotations.Test;
import pages.HomePage_Check;

public class T001_LoginTests_Check extends BaseTest {

        
    HomePage_Check homePageCheck;

    @Test
    public void pageTitleTest()
    {

    homePageCheck = new HomePage_Check(getDriver());

    System.out.println("[?] Called from test class -> " + driver);

    homePageCheck.getPageTitle();

        

    }

}
k314159
  • 5,051
  • 10
  • 32
Ramesh Korla
  • 64
  • 2
  • 3
  • 8