Selenium Parallel Execution NoSuchSessionException: invalid session id - java

I am trying to execute tests cases in parallel with TestNG. My test cases will use the same page and sometimes the same page objects. I close the webdriver after every scenario, however, I am getting an invalid session ID on random test cases. I checked the session IDs and it seems like the method is trying to use the most recently closed session to try and locate the web element. Does this mean that my drivers are not threadsafe? My DriverFactory class is as below
DriverFactory Class
public class DriverFactory{
private DriverFactory() {
}
private static DriverFactory instance = new DriverFactory();
public static DriverFactory getInstance() {
return instance;
}
ThreadLocal<WebDriver> driver = new ThreadLocal<WebDriver>();
public WebDriver getDriver() {
return driver.get();
}
public void setDriver(WebDriver driverParm) {
driver.set(driverParm);
}
public void closeBrowser() {
driver.get().close();
driver.remove();
}
}
In my Page Object classes, I created a constructor with the WebDriver. An example of my page object classes is as follows
Page Object Class
public class ExamplePageOne{
private WebDriver driver;
public ExmaplePageOne(WebDriver driver){
this.driver=driver;
PageFactory.initElements(driver,this);
}
#FindBy(how=How.XPATH, using = "//xpath here")
WebElement ButtonOne;
#FindBy(how=How.XPATH, using = "//xpath here")
WebElement ButtonTwo;
public void clickOnButtonOne(){
ButtonOne.click()
}
public void clickOnButtonTwo(){
ButtonTwo.click()
}
public void validate(){
//validate code here
}
}
In my step definition files, I create the instance of the page object class whenever I want to use it. I have also tried to create the instance out of the step methods but that did not work too.
Step Definition Class
public class StepDef{
#Given("Given step")
public void given_step(){
//block of code here
}
#When("When step")
public void when_step(){
ExamplePageOne pageOne = new ExamplePageOne(DriverFactory.getInstance.getDriver());
pageOne.clickOnButtonOne;
}
#Then("Then step")
public void then_step(){
ExamplePageOne pageOne = new ExamplePageOne(DriverFactory.getInstance.getDriver());
pageOne.validate;
}
}
In my hooks class, I set up and tear down the browser.
Hooks class
public class Hooks{
#Before
public void setup(){
DriverFactory.getInstance.setDriver(myOpenBrowserMethod);
WebDriver driver = DriverFactory.getInstance.getDriver();
driver.get("url here")
}
#After
public void teardown(){
DriverFactory.getInstance.closeBrowser();
}
}
Sorry if the code looks kinda weird, mostly typing this with memory. Is there something that I am doing wrong or missing? I thought my logic made sense but unfortunately not. Any help is appreciated, thank you!

Related

Multiple instances of browser opening up when running test through webdriver manager

I am new to Selenium, so basically whenever I ran my test, it would open up the URL which I have mentioned in my test. In my same Test I have also mentioned to fill the username and Password.
But somehow once the browser gets launched and redirect to the URL, it opens up another instance of blank browser failing my script that element not found.
Please help me here.
///////////////////////////////////////////////////////////////////////////
public class TruefillTest extends BaseClass {
public Truefill truefill()
{
WebDriver driver=InitializeDriver();
return new Truefill(driver);
}
#Test
public void userLoginIntoTheSystem()
{
truefill().dashBoard().Url();
truefill().dashBoard().EnterUsername("bjdgfe#swcwr.com");
truefill().dashBoard().EnterPassword("Test1234");
}
///////////////////////////////////////////////
public class Truefill {
private WebDriver driver;
public Truefill(WebDriver driver) {
this.driver=driver;
}
public DashBoardPage dashBoard()
{
return new DashBoardPage(driver);
}
////////////////////////////////////////////////////////////
public class DashBoardPage {
private final WebDriver driver;
By Username= By.xpath("//input[#name='name']");
By Password= By.xpath("//input[contains(#id,'exampleInputPassword1')]");
public DashBoardPage(WebDriver driver) {
this.driver=driver;
}
public void Url()
{
driver.get("https://rahulshettyacademy.com/angularpractice/");
}
public void EnterUsername(String username)
{
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
driver.findElement(Username).sendKeys(username);
}
public void EnterPassword(String password)
{
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
driver.findElement(Password).sendKeys(password);
}
////////////////////////////////////////////////////////////
public class BaseClass {
WebDriver driver;
public WebDriver InitializeDriver()
{
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
return driver;
}
}
Each call to the truefill() method initializes a new instance of WebDriver. Since your test is calling it multiple times, it will start a new browser instance on each line. Instead, store the DashboardPage in a local variable:
#Test
public void userLoginIntoTheSystem() {
DashBoardPage dashBoardPage = truefill().dashBoard();
dashBoardPage.Url();
dashBoardPage.EnterUsername("bjdgfe#swcwr.com");
dashBoardPage.EnterPassword("Test1234");
}
You might also want to use a setup method to initialize the Truefill instance, rather than creating it on demand:
private Truefill truefill;
#BeforeEach
public void initializeTruefill() {
WebDriver driver = InitializeDriver();
truefill = new Truefill(driver);
}
#Test
public void userLoginIntoTheSystem() {
DashBoardPage dashBoardPage = truefill.dashBoard();
dashBoardPage.Url();
dashBoardPage.EnterUsername("bjdgfe#swcwr.com");
dashBoardPage.EnterPassword("Test1234");
}
This assumes JUnit 5. If you're using JUnit 4, the annotation is #Before instead of #BeforeEach.

Can I reuse driver instances when running Cucumber scenarios in parallel?

I have a Cucumber TestNG test written that runs several tests in parallel. Currently I create the driver per scenario and my Hooks class looks like the following.
public class Hooks {
private TestContext testContext;
#Inject
public Hooks(TestContext testContext) {
this.testContext = testContext;
}
#Before
public void initializeTestContext(final Scenario scenario) {
this.testContext.initializeContext();
}
#After
public void after(final Scenario scenario) {
LOG.debug("Executing After Hook");
if (shouldScreenshot(scenario)) {
embedScreenshotToReport(scenario);
}
this.testContext.destroyContext();
}
#Before("#skip_scenario")
public void skipScenario(Scenario scenario) {
LOG.info("Skipping scenario: {}", scenario.getName());
Assume.assumeTrue(false);
}
private boolean shouldScreenshot(Scenario scenario) {
return scenario.isFailed() && Screenshot.isRequired();
}
private void embedScreenshotToReport(Scenario scenario) {
final byte[] screenshot = ((TakesScreenshot) testContext.getWebDriver()).getScreenshotAs(OutputType.BYTES);
scenario.embed(screenshot, "image/png");
}
}
My TestsContext class looks like the following.
#ScenarioScoped
public class TestContext extends WebUITest {
private static final String DEFAULT_DRIVER_NAME = "default";
private WebDriver webDriver;
public WebDriver getWebDriver() {
return this.webDriver;
}
public void initializeContext() {
System.out.println("DEBUGGING Test Context: driver created");
this.webDriver = initializeDriver(URL.getTestHostURL(), DEFAULT_DRIVER_NAME);
}
public void destroyContext() {
System.out.println("destory method called");
}
}
My Runner class with TestNG looks like the following.
glue = {"com.cucumber.test.glue.hook",
"com.cucumber.test.glue.stepdef" },
features = "features/MyFeature.feature",
plugin = {"pretty", "html:build/brazil-cucumber-tests/cucumber-pretty"},
strict = true)
public class ParallelRunner extends AbstractTestNGCucumberTests {
#Override
#DataProvider(parallel = true)
public Object[][] scenarios() {
return super.scenarios();
}
}
I was thinking of reusing the same driver instance across parallel tests but is that possible? If so, how can I instantiate the driver? Cucumber doesn't seem to have a beforeAll kind of method and when I use beforeAll of testNG in Hooks class, it doesn't seem to get called. Any advice would be much appreciated.
Reusing driver session across multiple thread doesn't makes sense. Instead reusing driver session between test/scenarios within same thread is possible. For that you need to update your code to use thread local driver instance.
Simplest way is to use qaf-cucumber with property selenium.singletone = 1. This stetting will reuse driver session for tests methods/scenarios running in same thread. If you change your mind and want to use new session for each scenario set selenium.singletone=Methods
Anywhere you want driver object, you can get from testbase:
WebDriver webDriver = new WebDriverTestBase().getDriver();
your code may look like below:
public class Hooks {
private TestContext testContext;
#Inject
public Hooks(TestContext testContext) {
this.testContext = testContext;
}
#After
public void after(final Scenario scenario) {
LOG.debug("Executing After Hook");
if (shouldScreenshot(scenario)) {
embedScreenshotToReport(scenario);
}
this.testContext.destroyContext();
}
#Before("#skip_scenario")
public void skipScenario(Scenario scenario) {
LOG.info("Skipping scenario: {}", scenario.getName());
Assume.assumeTrue(false);
}
private boolean shouldScreenshot(Scenario scenario) {
return scenario.isFailed() && Screenshot.isRequired();
}
private void embedScreenshotToReport(Scenario scenario) {
final byte[] screenshot = ((TakesScreenshot) new WebDriverTestBase().getDriver().getScreenshotAs(OutputType.BYTES);
scenario.embed(screenshot, "image/png");
}
}
#ScenarioScoped
public class TestContext extends WebUITest {
//nothing to do for driver management!....
//just set driver.name property
//code below is only for compatibility
public WebDriver getWebDriver() {
return new WebDriverTestBase().getDriver();
}
public void destroyContext() {
System.out.println("destory method called");
}
}
You can take benefit of other features of QAF like
listeners
External data-providers
Locator repository
Wait/Assertion/verification with auto screenshot
pure TestNG BDD implementation

Null pointer exception in selenium cucumber JUnit Framework

Can someone tell the issue in my code for null pointer exception?
Error Message in console
=>test.pages.HomePage#31e75d13<=
[31mFailed scenarios:[0m
[31mE2E_Test.feature:3 [0m# Scenario: Test scenario
1 Scenarios ([31m1 failed[0m)
10 Steps ([31m1 failed[0m, [36m8 skipped[0m, [32m1 passed[0m)
0m12.461s
java.lang.NullPointerException
at org.openqa.selenium.support.pagefactory.DefaultElementLocator.findElement(DefaultElementLocator.java:69)
at org.openqa.selenium.support.pagefactory.internal.LocatingElementHandler.invoke(LocatingElementHandler.java:38)
at com.sun.proxy.$Proxy17.sendKeys(Unknown Source)
at test.pages.HomePage.enterSearchText(HomePage.java:31)
at stepDefinitions.Steps.he_search_for(Steps.java:49)
at ✽.When he search for "test"(E2E_Test.feature:5)
Although I am getting the driver object and its not coming as Null as well but still getting a null pointer exception.
I am trying to run the selenium webdriver code to automate some test case. Here i am trying to open google.com and wants to enter some text in search box but after opening the google.com, when the execution reaches searchtextbox.sendkeys("test"), it gives null pointer exception. I tried debugging it to see if the homepage class object is coming as null or not but its showing the value and not null.
This is the test base class that i am using to initiate the google site and maximize the code
public class TestBase {
public static WebDriver driver;
public static Properties prop;
public static EventFiringWebDriver e_driver;
public static WebEventListener eventListener;
public TestBase() {
try {
prop = new Properties();
FileInputStream ip = new FileInputStream(System.getProperty("user.dir") + "/src/main/java/test" +
"/config/config.properties");
prop.load(ip);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// This method is used to initiatize the site url
public static void initialization(String url) {
String browserName = prop.getProperty("browser");
if (browserName.equals("chrome")) {
System.setProperty("webdriver.chrome.driver", "C:\\Users\\test\\Downloads\\driver\\chromedriver.exe");
driver = new ChromeDriver();
}
e_driver = new EventFiringWebDriver(driver);
// Now create object of EventListerHandler to register it with EventFiringWebDriver
eventListener = new WebEventListener();
e_driver.register(eventListener);
driver = e_driver;
driver.manage().window().maximize();
driver.manage().deleteAllCookies();
driver.manage().timeouts().pageLoadTimeout(TestUtil.PAGE_LOAD_TIMEOUT, TimeUnit.SECONDS);
driver.manage().timeouts().implicitlyWait(TestUtil.IMPLICIT_WAIT, TimeUnit.SECONDS);
if (url == "google") {
driver.get(prop.getProperty("url"));
}
}
}
// Steps Definition file (Steps.java): This is the step defintion file // there is a function he_search_for called where the exception occurs
public class Steps extends TestBase {
WebDriver driver;
TestUtil testUtil;
HomePage homePage;
#Given("^user is on google home page$")
public void user_is_on_google_home_page() throws Throwable {
initialization("google");
testUtil = new TestUtil();
homePage = new HomePage(driver);
}
#When("^he search for \"([^\"]*)\"$")
public void he_search_for(String arg1) throws InterruptedException {
System.out.print("=>" + homePage + "<=");
homePage.enterSearchText();
}
}
// HomePage Class is used to define all the page elements here in this class, i used enterSearchText function to enter the text in a search box.
public class HomePage extends TestBase {
#FindBy(name = "q")
WebElement searchTextBox;
WebDriver driver;
// Initializing the Page Objects:
public HomePage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
public void enterSearchText() {
searchTextBox.sendKeys("Test");
}
}
Problem lies within your code design pattern between Classes Steps & TestBase. Please note
First, Class Steps is extending TestBase which already has WebDriver variable declared & initialized in it. So you do not need to again define WebDriver instance with in Steps. So please remove "WebDriver driver;" from below peace of code.
public class Steps extends TestBase {
WebDriver driver;
TestUtil testUtil;
Second, Please do not declare WebDriver as static variable. Kindly declare it as Non-static as Keeping static may create problem during parallel execution as well.
public class TestBase {
public WebDriver driver;
Making WebDriver instance as non-static and having it as Thread safe
TestBase.java
public class TestBase {
public WebDriver driver;
public static Properties prop;
// This method is used to initiatize the site url
public synchronized void initialization(String url) {
String browserName = prop.getProperty("browser");
if (browserName.equals("chrome")) {
System.setProperty("webdriver.chrome.driver", "C:\\Users\\test\\Downloads\\driver\\chromedriver.exe");
driver = new ChromeDriver();
DriverManager.setWebDriver(driver);
}
}
}
DriverManager.java
import org.openqa.selenium.WebDriver;
public class DriverManager {
public static ThreadLocal<WebDriver> dr = new ThreadLocal<WebDriver>();
public static WebDriver getDriver() {
return dr.get();
}
public static void setWebDriver(WebDriver driver) {
dr.set(driver);
}
}
The problem is here
public class Steps extends TestBase {
WebDriver driver;
TestUtil testUtil;
HomePage homePage;
#Given("^user is on google home page$")
public void user_is_on_google_home_page() throws Throwable {
initialization("google");
testUtil = new TestUtil();
homePage = new HomePage(driver);
}
#When("^he search for \"([^\"]*)\"$")
public void he_search_for(String arg1) throws InterruptedException {
System.out.print("=>" + homePage + "<=");
homePage.enterSearchText();
}
}
The WebDriver driver is null. You initialized WebDriver in initialization("google") method but you don't assign the value of created WebDriver to your driver
Additional line of code might help you.
#Given("^user is on google home page$")
public void user_is_on_google_home_page() throws Throwable {
initialization("google");
this.driver = TestBase.driver; //asign initialized WebDriver to this instance variable
testUtil = new TestUtil();
homePage = new HomePage(driver);
}
You can also remove the local WebDriver driver variable. Since TestBase contains static WebDriver, you can just use it directly since you use inheritance.
However, I highly suggest reading about WebDriverFactory or any similar term, like WebDriverManager. Anything to handle WebDriver instantiation without creating a static WebDriver. It will cause a lot of issues in the future with parallel execution.

Using Common Selenium WebDriver instance

I want use a common WebDriver instance across all my TestNG tests by extending my test class to use a base class as shown below but it doesn't seem to work :
public class Browser {
private static WebDriver driver = new FirefoxDriver();
public static WebDriver getDriver()
{
return driver;
}
public static void open(String url)
{
driver.get(url);
}
public static void close()
{
driver.close();
}
}
I want to use the WebDriver in my test class as shown below, but I get the error message :
The method getDriver() is undefined for the type GoogleTest:
public class GoogleTest extends Browser
{
#Test
public void GoogleSearch() {
WebElement query = getDriver().findElement(By.name("q"));
// Enter something to search for
query.sendKeys("Selenium");
// Now submit the form
query.submit();
// Google's search is rendered dynamically with JavaScript.
// Wait for the page to load, timeout after 5 seconds
WebDriverWait wait = new WebDriverWait(getDriver(), 30);
// wait.Until((d) => { return d.Title.StartsWith("selenium"); });
//Check that the Title is what we are expecting
assertEquals("selenium - Google Search", getDriver().getTitle().toString());
}
}
The problem is that your getDriver method is static.
Solution #1: Make method non-static (this will either need to make the driver variable non-static as well, or use return Browser.getDriver(); )
public WebDriver getDriver() {
return driver;
}
Or, call the getDriver method by using Browser.getDriver
WebElement query = Browser.getDriver().findElement(By.name("q"));
You need to start your driver, one of many solution is to try #Before to add, Junit will autorun it for you.
public class Browser {
private WebDriver driver;
#Before
public void runDriver()
{
driver = new FirefoxDriver();
}
public WebDriver getDriver()
{
return driver;
}
public void open(String url)
{
driver.get(url);
}
public void close()
{
driver.close();
}
}

Checking page status programmatically

I have been writing Selenium test for web application and there seem to be multiple instances of Internal Server Error in application in case of Internal Server Error, application displays custom error page and and error id is displayed to user to pursue matter with technical team, in case user encounter it.
This makes it a little laborious to debug the test failures during Selenium execution.
I was thinking to use some mechanism to keep polling a page with each step executed in test to find if there was any instance of Internal Server error, And this is when I came across Junit Rule and thought of writing a custom annotation for it, some thing like -
public class SelTestCase {
protected WebDriver driver;
#Before
public void startDriver() {
driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("http://www.google.com/");
}
#After
public void closeDriver() {
driver.quit();
}
}
public class GoogleSearchTest extends SelTestCase {
#Rule
PageChecker pageChecker = new PageChecker();
#Test
#CheckPage
public void testGoogleSearch() {
GoogleHomePage googleHomePage = PageFactory.initElements(driver,
GoogleHomePage.class);
googleHomePage.searchGoogle("Selenium HQ");
assert driver.getPageSource().contains("seleniumhq") : "Selenium headquarter search failed";
}
}
SelTestCase class creates instance of WebDriver to execute test, And here is the PageChecker class -
public class PageChecker extends SelTestCase {
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
public #interface CheckPage {
// page check should take place here (Though not sure if it is right place)
// like if(driver.getPageSource.contains("Internal Server Error") {throw Exception ("Err")}
}
}
This is what I am stuck with, how do I proceed with CheckPage annonations?
IMHO there are two solutions to your problems. If the feature is only needed by a small part of your tests, then I would not use a rule. Instead add a single line errorChecker.checkPage(driver) to each tests and implement the check in this method.
If you need it for almost all your tests:
Convert SelTestCase to a rule by extending ExternalResource.
public class WebDriverRule extends ExternalResource {
public WebDriver driver;
#Override
protected void before() {
driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("http://www.google.com/");
}
#Override
protected void after() {
driver.quit();
}
}
Add the page check code to the rule by extending Verifier.
public class PageChecker extends Verifier {
private WebDriverRule webDriverRule;
private enabled = true;
public PageChecker(WebDriverRule webDriverRule) {
this.webDriverRule = webDriverRule;
}
public void disable() {
this.enabled = false;
}
#Override
public void verify() {
if(enabled && notValid())
throw new AssertionError("foo");
}
private boolean notValid() {
WebDriver driver = webDriverRule.driver;
//do something with driver
}
}
Use org.junit.rules.RuleChain to control the execution order of the two rules.
public class GoogleSearchTest {
private WebDriverRule webDriverRule = new WebDriverRule();
private PageChecker pageChecker = new PageChecker(webDriverRule);
#Rule
public RuleChain driverAroundPageChecker
= RuleChain.outerRule(webDriverRule).around(pageChecker);
#Test
public void testGoogleSearch() {
GoogleHomePage googleHomePage = PageFactory.initElements(driver,
GoogleHomePage.class);
googleHomePage.searchGoogle("Selenium HQ");
assert driver.getPageSource().contains("seleniumhq") : "Selenium headquarter search failed";
}
#Test
public void testWithouPageCheck() {
pageChecker.disable();
//here is your real test
}
}

Categories