I have a class in my Selenium framework that is responsible for getting a fresh driver object and passing it to the tests for use.
But for some reason, it's returning a null error and I can't figure out why.
Here is the class:
DriverManager
private WebDriver driver;
public WebDriver getDriver() {
WebDriverFactory factory = new WebDriverFactory();
driver = factory.CreateBrowser(CHOSEN_BROWSER);
return driver;
}
My Factory class:
public WebDriver CreateBrowser(BrowserType browser)
{
switch(browser) {
case Chrome:
return createChromeDriver();
case Firefox:
return createFirefoxDiver();
case InternetExplorer:
return createInternetExplorerDriver();
default:
System.out.println("Invalid browser type");
return null;
}
}
My tests where I am creating an instance of the DriverManager:
BaseTest:
public DriverManager driverManager;
public WebDriver driver;
#Before
public void CreateDriver()
{
driver = driverManager.getDriver();
Pages.init(driver);
Logging.info("TEST STARTING: " + name.getMethodName());
}
But as soon as it hits the first line in the CreateDriver method. I recieved a null pointer error and the tests fail with a NullPointerException.
I assume your driverManager attribute is null. That's why driver = driverManager.getDriver(); is causing a NPE.
I guess public DriverManager driverManager = new DriverManager(); would solve this already.
Related
Here is my code:
public class BasePage extends PageObject {
#Managed
WebDriver driver;
public Alert waitingForAlert() throws InterruptedException {
Alert al = driver.switchTo().alert();
return al;
}
I'm getting the error
java.lang.NullPointerException: Cannot invoke "org.openqa.selenium.WebDriver.switchTo()" because "this.driver" is null
You get this error only because of using driver before creating an instance of it.
Let's suppose you want to run this test case on Chrome and your PageObject class doesn't have Chrome Driver instance (if yes it will create new chrome driver instance again which is not supposed to do)
WebDriver driver;
public Alert waitingForAlert() throws InterruptedException {
Alert al = driver.switchTo().alert();
return al;
}
The nullPointer exception comes from this line: driver.switchTo().alert();
in order to use driver you should create an instance of it. Meaning you can do it by typing:
class BasePage extends PageObject {
#Managed
WebDriver driver;
public void setDriver()
{
System.setProperty("webdriver.chrome.driver","<Path of ChromeDriver.exe here>");
driver = new ChromeDriver(); // Creating new 'ChromeDriver' instance
}
public Alert waitingForAlert() throws InterruptedException {
setDriver();
Alert al = driver.switchTo().alert();
return al;
}
}
What you're seeing is that driver is still null because BasePage has not yet been instantiated. You will have to override the navigateTo method in some way to create a driver.
class BasePage extends PageObject {
#Managed
WebDriver driver;
public Alert waitingForAlert() throws InterruptedException {
Alert al = driver.switchTo().alert();
return al;
}
public void navigateTo() { // your code here ... }
}
I"m trying to build selenium with extent report but i could not get the save screenshot function working because i cannot reference the WebDriver object in the ITestListener class. Below is my sample code:
Test Runner.java:
#Listeners({com.peterwkc.Listener.TestListener.class})
public class ChromeTestManager {
private WebDriverManager webDriverManager = new WebDriverManager();
private WebDriver driver;
private LoginPages loginPages;
private AdminPages adminPages;
#BeforeClass
//#Parameters({"browser"})
public void setupTest(/*String browser*/) throws MalformedURLException {
System.out.println("BeforeMethod is started. " + Thread.currentThread().getId());
// Set & Get ThreadLocal Driver with Browser
webDriverManager.createDriver("chrome");
driver = webDriverManager.getDriver();
// Pages Object Initialization
loginPages = PageFactory.initElements(driver, LoginPages.class);
logoutPages = PageFactory.initElements(driver, LogoutPages.class);
adminPages = PageFactory.initElements(driver, AdminPages.class);
}
#DataProvider(name = "loginCredentials")
public static Object[][] getLoginCredentials() {
return new Object [][] {{ "Admin123", "admin123" }, {"testUser", "test"}, {"test", "test"}};
}
#Test(groups= {"Login"}, description="Invalid Login", priority = 0, dataProvider = "loginCredentials", invocationCount = 3)
public void login_invalid(String username, String password) {
loginPages.login_invalid(driver, username, password);
}
}
TestListener.java
public class TestListener implements ITestListener {
//Extent Report Declarations
private static ExtentReports extent = ExtentManager.createInstance();
private static ThreadLocal<ExtentTest> test = new ThreadLocal<>();
public TestListener() {
}
#Override
public synchronized void onTestFailure(ITestResult result) {
System.out.println((result.getMethod().getMethodName() + " failed!"));
test.get().fail("Exception Error : \n" + result.getThrowable());
/*String feature = getClass().getName();
String screenShot;
try {
screenShot = CaptureScreenshot.captureScreen(driver, CaptureScreenshot.generateFileName(feature));
test.get().addScreenCaptureFromPath(screenShot);
test.get().log(Status.FAIL, screenShot);
} catch (IOException ex) {
LogManager.logger.log(Level.INFO, "Exception: " + ex.getMessage());
}*/
}
}
Questions:
How to pass the WebDriver object from TestRunner.java to TestListener
class?
How to save screenshot in extent report 3?
Anything wrong with my code?
please help, thanks in advance!
Below are the steps to do this :
1 : Passing WebDriver object to Listener class
First create below method in ChromeTestManager class or at any another location from where you can call it, here suppose that it is present in ChromeTestManager class:
public static ITestContext setContext(ITestContext iTestContext, WebDriver driver) {
iTestContext.setAttribute("driver", driver);
return iTestContext;
}
It will set the driver object to the TestContext.
Now change your #BeforeClass setUp method to accept parameter ITestContext, below is the code :
public class ChromeTestManager {
private WebDriverManager webDriverManager = new WebDriverManager();
private WebDriver driver;
private LoginPages loginPages;
private AdminPages adminPages;
private static ITestContext context; // creating a ITestContext variable
#BeforeClass
//#Parameters({"browser"})
public void setupTest(ITestContext iTestContext) throws MalformedURLException {
System.out.println("BeforeMethod is started. " + Thread.currentThread().getId());
// Set & Get ThreadLocal Driver with Browser
webDriverManager.createDriver("chrome");
driver = webDriverManager.getDriver();
this.context = setContext(iTestContext, driver); // setting the driver into context
// Pages Object Initialization
loginPages = PageFactory.initElements(driver, LoginPages.class);
logoutPages = PageFactory.initElements(driver, LogoutPages.class);
adminPages = PageFactory.initElements(driver, AdminPages.class);
}
When you run this, it will run smoothly and will not produce an error (If you are thinking that how I will pass ITestcontext context, It is handled internally)
Now the driver has been added as an object to the ITestcontext ;
Now Accessing the driver in Listener :
#Override
public synchronized void onTestFailure(ITestResult result) {
WebDriver driver = (WebDriver) result.getTestContext().getAttribute("driver"); // here we are accessing the driver object that we added in Test class
}
2. Saving ScreenShot in extent report 3 (I am using dependency 3.1.5 in maven)
#Override
public synchronized void onTestFailure(ITestResult result) {
System.out.println("!!!!!!!!!!!!!!!!!!!! Test Failed !!!!!!!!!!!!!!!!!!!!");
WebDriver driver = (WebDriver) result.getTestContext().getAttribute("driver"); // accessing driver here
String feature = getClass().getName();
String screenShot;
try {
screenShot = CaptureScreenshot.captureScreen(driver, CaptureScreenshot.generateFileName(feature));
test.addScreenCaptureFromPath(screenShotPath); // I am assuming that the "screenShot" is fully qualified path with extension e.g "C:\Users\12345\Desktop\sfgfdh.PNG"
} catch (IOException ex) {
LogManager.logger.log(Level.INFO, "Exception: " + ex.getMessage());
}
}
3. Is there anything wrong with your code ?
No
You just need driver in Listener class and while adding screenshot in extent report ,
make sure that the path to screenshot is correct and is fully qualified path with extension.
Please let me know if you face an issue in this.
First of all don't instantiate Your webDriver in #BeforeClass, because this is called only once as annotation say before class, try using interface ITestListener and using beforeInvocation implementation for initialisation of WebDriver.
Second, You can't call PageFactory for all PageObjects at once, how do You think all 3 pages are initialised at once, this should be achieved in constructor for each page object, and when You init you page object (new Login) the elements are initialised as well, so this is not ok:
// Pages Object Initialization
loginPages = PageFactory.initElements(driver, LoginPages.class);
logoutPages = PageFactory.initElements(driver, LogoutPages.class);
adminPages = PageFactory.initElements(driver, AdminPages.class);
Third I don't see initialisation of ExtentReport test. It should looks something like this:
ExtentTest extentTest = ExtentTestManager.startTest(method.getName(), "");
Here is an example part of code from my implementation of calling screenshot, I'am calling it from afterInvocation, because I'm using concurrent driver initialisation, and so it had to be from here, but also can be achived via onTestFailure implementation:
public synchronized void afterInvocation(IInvokedMethod method, ITestResult testResult){
if (method.isTestMethod() && testResult.getStatus()==2) {
File scrFile = (dataMethod.getAndroidDriver()).getScreenshotAs(OutputType.FILE);
String dest = System.getProperty("user.dir") + "/resources/screenshots/" + dataMethod.getDriver().getSessionId() + ".png";
File destination = new File(dest);
try {
FileUtils.copyFile(scrFile, destination);
dataMethod.setScreenshotPath(destination.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
System.err.println("Path:" + dataMethod.getScreenshotPath());
}
You have to think more about structure,
Hope this helps...
Multiple Browser Profiles for Multiple (Concurrent) Test Execution?
Is this even possible?
For example I can execute two tests at the same time but when two tests open at the same time within the same browser they seem to share the same cookies.
Please find my main Browser factory class listed below, can anyone advise the best way to alter my code, or settings required which will enable me to meet my objective?
Thanks for your help
public class BrowserFactory implements ISuiteListener {
private static WebDriver webdriver;
public static WebDriver getDriver() throws Exception {
try {
Properties p = new Properties();
FileInputStream fi = new FileInputStream(Constant.CONFIG_PROPERTIES_DIRECTORY);
p.load(fi);
String browserName = p.getProperty("browser");
switch (browserName) {
//firefox setup
case "firefox":
if (null == webdriver) {
System.setProperty("webdriver.gecko.driver", Constant.GECKO_DRIVER_DIRECTORY);
webdriver = new FirefoxDriver();
}
break;
//chrome setup
case "chrome":
if (null == webdriver) {
System.setProperty("webdriver.chrome.driver", Constant.CHROME_DRIVER_DIRECTORY);
DesiredCapabilities caps = DesiredCapabilities.chrome();
LoggingPreferences logPrefs = new LoggingPreferences();
logPrefs.enable(LogType.BROWSER, Level.ALL);
caps.setCapability(CapabilityType.LOGGING_PREFS, logPrefs);
webdriver = new ChromeDriver(caps);
}
break;
//IE setup
case "ie":
if (null == webdriver) {
System.setProperty("webdriver.ie.driver", Constant.IE_DRIVER_DIRECTORY);
webdriver = new InternetExplorerDriver();
}
break;
}
} catch (Exception e) {
System.out.println("Unable to load browser! - Exception: " + e.getMessage());
}
return webdriver;
}
#AfterClass
public void quitDriver() throws Exception {
if (null != webdriver) {
getDriver().manage().deleteAllCookies();
webdriver.quit();
webdriver = null;
}
// Output the time when a test class has ended
String tempTimeEndClass = new SimpleDateFormat("hh.mm.ss").format(new Date());
System.out.println("\nTEST CLASS END TIME: " + tempTimeEndClass);
}
}
Yes, of course it is possible!
They share the same cookies because the instance of the WebDriver you are creating is static, trying removing the static modifier so on every launch of the webdriver you get a unique instance.
public webdriver driver; public WebDriver getDriver() throws Exception
{ }
If the above isn't enough and you want to do additional things with the profiles; just pass it as a parameter via xml or as a String var in the method:
currentProfile = "user-data-dir=/path/to/your/custom/profile";
ChromeOptions options = new ChromeOptions();
options.addArguments(currentProfile);
Again, careful here currentProfile needs to be an instance variable not a static one!
Best of luck!
I am trying to run Selenium Webdriver tests in parallel on a single machine, using TestNG. I have 3 #Test methods, where 3 different users log in to the same application and reach the home page. I need #Test methods to run in parallel, and write to an ExtentReports report.
My problem is, despite 3 completely different methods in different classes, one of the users will be logged into 2 out of 3 of the browsers, leaving a user out.
The login method is located in a PageFactory page object class.
Here are my 3 test methods:
#Test(enabled = true, priority = 0)
public void JohnLogin() throws Exception {
ExtentTest t = ClientReportFactory.getTest();
try {
Login objLogin = new Login(getDriver());
String username = "John";
String password = "Password";
objLogin.SignIn(username, password);
HomePage objHomePage = new HomePage(getDriver());
assertTrue(objHomePage.clientName.getText().contains("John"));
} catch (Exception e) {
}
}
#Test(enabled = true, priority = 1)
public void BobLogin() throws Exception {
ExtentTest t = ClientReportFactory.getTest();
try {
Login objLogin = new Login(getDriver());
String username = "Bob";
String password = "Password";
objLogin.SignIn(username, password);
HomePage objHomePage = new HomePage(getDriver());
assertTrue(objHomePage.clientName.getText().contains("Bob"));
} catch (Exception e) {
}
}
#Test(enabled = true, priority = 2)
public void SamLogin() throws Exception {
ExtentTest t = ClientReportFactory.getTest();
try {
Login objLogin = new Login(getDriver());
String username = "Sam";
String password = "Password";
objLogin.SignIn(username, password);
HomePage objHomePage = new HomePage(getDriver());
assertTrue(objHomePage.clientName.getText().contains("Sam"));
} catch (Exception e) {
}
}
So, if I pause the tests on the Homepage. I will have 2 browser windows opened as "John", one "Bob" and no "Sam"... Causing failures.
Here's the PageFactory Object's login method.
public void SignIn(String strUsername, String strPassword) throws InterruptedException {
WebDriverWait wait = new WebDriverWait(driver, 15);
username.clear();
username.sendKeys(strUsername);
password.clear();
password.sendKeys(strPassword);
submit.click();
wait.until(ExpectedConditions.visibilityOf(homePagePanel));
}
At first I was sure the problem was in the #BeforeMethod threading (As in, the tests were in a different thread than the #Before and #After). But I don't see how that could be the case. The Base Test method successfully opens and closes 3 browsers. It just seems like the #Test methods use each other's data! But just in case, here's my #Before and #After, with my Threading code.
public class BaseTest {
public String browser;
private ThreadLocal<WebDriver> threadedDriver = new ThreadLocal<WebDriver>();
#BeforeMethod(alwaysRun = true)
#Parameters({ "browser"})
public void setup(String browser)throws MalformedURLException,
InterruptedException {
WebDriver driver = null;
if (browser.equalsIgnoreCase("Internet Explorer")) {
System.setProperty("webdriver.ie.driver", "C:\\Selenium\\IEDriverServer.exe");
driver = new InternetExplorerDriver();
} else if (browser.equalsIgnoreCase("Firefox")) {
System.setProperty("webdriver.gecko.driver", "C:\\Selenium\\geckodriver.exe");
driver = new FirefoxDriver();
} else if (browser.equalsIgnoreCase("chrome")) {
System.setProperty("webdriver.chrome.driver", "C:\\Selenium\\chromedriver.exe");
driver = new ChromeDriver();
} else if (browser.equalsIgnoreCase("MicrosoftEdge")) {
System.setProperty("webdriver.edge.driver", "C:\\Selenium\\MicrosoftWebDriver.exe");
driver = new EdgeDriver();
}
setWebDriver(driver);
this.browser = browser;
ClientReportFactory.getTest(ExtentTestName, ExtentTestDescription);
baseURL = "testApp.com";
driver.get(baseURL);
driver.manage().window().maximize();
}
public WebDriver getDriver(){
return threadedDriver.get();
}
public void setWebDriver(WebDriver driver) {
threadedDriver.set(driver);
}
#AfterMethod
public void afterMethod() {
ClientReportFactory.closeTest(ExtentTestName, ExtentTestDescription);
getDriver().quit();
threadedDriver.set(null);
}
#AfterSuite
public void afterSuite() {
ClientReportFactory.closeReport();
if (getDriver() != null) {
getDriver().quit();
} else {
System.out.println("Drivers already closed");
}
}
Assuming that all of your #Test methods are in different classes, I am guessing that the problem is perhaps due to the fact that your ThreadLocal variable is NOT STATIC but is an instance variable. This causes the behaviour to be per thread per instance rather than the desired behaviour viz., per thread across all instances. You can refer to this StackOverFlow thread for a better explanation on this.
You would resort to using an instance variant of ThreadLocal if and only if all your #Test methods belong to the same test class (Because now you are only trying to ensure that the class level data member WebDriver is shared in a thread safe manner across all the test methods that belong to the same test class)
So if each of your #Test methods reside in its own Test class, then please try changing:
private ThreadLocal<WebDriver> threadedDriver = new ThreadLocal<WebDriver>();
to
private static ThreadLocal<WebDriver> threadedDriver = new ThreadLocal<WebDriver>();
You could try this.
public class DriverFactory(){
private static ThreadLocal<WebDriver> driverThread;
public WebDriver driver;
#Parameters("browser")
public WebDriver instantiateDriverObject(String browser) {
DriverFactory factory = new DriverFactory();
driver = factory.createInstance(browser); //Driver instantiation goes here
driverThread = new ThreadLocal<WebDriver>() {
#Override
protected WebDriver initialValue() {
webDriverPool.add(driver);
return driver;
}
};
return driver;
}
public WebDriver getDriver() {
return driverThread.get();
}
}
I have created a simple factory method that creates a HtmlUnitDriver in a specified mode. For example
public static HtmlUnitDriver createHtmlUnitDriver(String browserMode) {
switch(browserMode.toLowerCase()) {
case "chrome":
return new HtmlUnitDriver(DesiredCapabilities.chrome());
case "firefox":
return new HtmlUnitDriver(DesiredCapabilities.firefox());
case "ie":
case "internet explorer":
return new HtmlUnitDriver(DesiredCapabilities.internetExplorer());
case default:
return new HtmlUnitDriver(true);
}
}
I'd like to write a quick unit test of this method, but I can't see how to tell which "mode" the returned HtmlUnitDriver is in. How would I do this?
Using reflection, you can get the private field webClient
#Test
public void test() throws Exception {
WebDriver driver = new HtmlUnitDriver(BrowserVersion.CHROME);
WebClient webClient = (WebClient) get(driver, "webClient");
System.out.println(webClient.getBrowserVersion());
System.out.println(webClient.getBrowserVersion().isIE());
driver.close();
}
private static Object get(Object object, String field) throws Exception {
Field f = object.getClass().getDeclaredField(field);
f.setAccessible(true);
return f.get(object);
}