moad hamouch
moad hamouch

Reputation: 11

TestNG parallel DataProvider in tabs

I'm very new using TestNG and Java programming in general and I have a question in regards to runing testcases with dataprovider in parallel,

in order to run dataprovider test cases in multiple tabs in a single chrome window instead of many windows, I used selenium 4 which allows to open and switch to new tab using the folowing code

driver.switchTo().newWindow(WindowType.TAB);

so, I wrote this code which is successfully run the test cases in sequence mode, but when i run it in parallel mode, it implements each line separately, before moving on to the other line,

consequently, it opens the 3 tabs at the same time, afterwards, it opens the link in the last tab 3 times, and finally it tries to do the 3 searches at the same time, and therefore it cannot execute them.

I find as a result that testNG create a single webdriver to control all the tabs, which is logical to cause the failure of the tests in parallel mode.

https://i.sstatic.net/LV5KS.jpg

This is the code:

package com.mycompany.app;

import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WindowType;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class testTab {

    WebDriver driver;

    @BeforeTest
    public void beforeTest() {
        System.setProperty("webdriver.chrome.driver", "C:\\Browsers drivers\\chromedriver.exe");
        driver = new ChromeDriver();
    }

    @BeforeMethod
    public void newTab() {
        driver.switchTo().newWindow(WindowType.TAB);
    }

    @Test(dataProvider = "getData")
    public void testAmazon1(String search_word) {
        driver.get("https://www.google.com/");
        driver.findElement(By.xpath("//input[@name='q']")).sendKeys(search_word + (Keys.RETURN));
    }

    @DataProvider(parallel=true)
    public Object[][] getData() {
        Object[][] data = new Object[3][1];
        data[0][0] = "bihi";
        data[1][0] = "boutfounast";
        data[2][0] = "hmad l9rran";
        return data;
    }

}

This is XML file

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="My Sample Suite" parallel="methods" thread-count="3">


<test name="Amazon test">
    <classes>
        <class name="com.mycompany.app.testTab"></class>  
    </classes>
</test>


</suite>

Is there a way to create a webdriver for each test? I am sorry for not being precise and thank you for your answers.

Upvotes: 0

Views: 1705

Answers (2)

Krishnan Mahadevan
Krishnan Mahadevan

Reputation: 14746

There are a few issues with your test code.

  • You have a class level data member WebDriver which gets shared across all @Test methods in the same test class and also across all iterations of a data driven @Test method.
  • You are making use of a @BeforeTest method to initialise your webdriver instance, which means that it gets initialised ONLY once per <test> tag and if there are any other downstream test classes that extend this test class, they will face NullPointerException.

Is there a way to create a webdriver for each test?

Yes its very much possible. You need to work with ThreadLocal wherein you instantiate the WebDriver either in a TestNG listener (or) in a @BeforeMethod method (because the @BeforeMethod is guaranteed to run on the same thread as the @Test)

Here's how you go about doing this.

First create a driver factory that can produce WebDriver instances for you.

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;

class LocalDriverFactory {
    static WebDriver createInstance(String browserName) {
        WebDriver driver = null;
        if (browserName.toLowerCase().contains("firefox")) {
            driver = new FirefoxDriver();
            return driver;
        }
        if (browserName.toLowerCase().contains("internet")) {
            driver = new InternetExplorerDriver();
            return driver;
        }
        if (browserName.toLowerCase().contains("chrome")) {
            driver = new ChromeDriver();
            return driver;
        }
        return driver;
    }
}

Now lets create a driver manager, that acts as means to interact with the instantiated webdriver

import org.openqa.selenium.WebDriver;

public class LocalDriverManager {
  private static ThreadLocal<WebDriver> webDriver = new ThreadLocal<>();

  public static WebDriver getDriver() {
    return webDriver.get();
  }

  static void setWebDriver(WebDriver driver) {
    webDriver.set(driver);
  }

  public static void cleanupDriver() {
    webDriver.get().quit();
    webDriver.remove();
  }
}

Here's how your test class would now look like

import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class TestTab {

  @BeforeMethod
  public void beforeMethod() {
    LocalDriverManager.setWebDriver(LocalDriverFactory.createInstance("chrome"));
  }

  @Test(dataProvider = "dp")
  public void testMethod(String searchWord) {
    WebDriver driver = LocalDriverManager.getDriver();
    driver.get("https://www.google.com/");
    driver.findElement(By.xpath("//input[@name='q']")).sendKeys(searchWord + (Keys.RETURN));
  }

  @DataProvider(name = "dp", parallel = true)
  public Object[][] getData() {
    return new Object[][] {{"bihi"}, {"boutfounast"}, {"hmad l9rran"}};
  }

  @AfterMethod
  public void afterMethod() {
    LocalDriverManager.cleanupDriver();
  }
}

I created a detailed blog that explains all of this. You can read more about it here

As an improvisation, I also created a simple maven library called autospawn that abstracts out all of this browser instantiation and clean up logic from you and lets you work with a webdriver by using a custom annotation.

You can get more information about it from here: https://github.com/RationaleEmotions/autospawn

Upvotes: 1

Muzzamil
Muzzamil

Reputation: 2881

You need to use dataproviderthreadcount. The thread-count values is not required. See details here.

You can use Tesng.xml as mentioned below :

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="My Sample Suite" parallel="methods" data-provider-thread-count="3">


<test name="Amazon test">
    <classes>
        <class name="com.mycompany.app.testTab"></class>  
    </classes>
</test>


</suite>

Upvotes: 0

Related Questions