Search code examples
javaselenium-webdriverwebautomationselenium-java

How to avoid "NullPointerException" while referencing the driver from ThreadLocal to Test Class?


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.


Solution

  • 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();
    
            
    
        }
    
    }