How to avoid empty extra browser opens when runnin

2019-05-31 07:33发布

问题:

While I'm running parallel test cases using TestNG and selenium-webdriver, there are some browser instances that open empty and doesn't close even after the execution finish. I have read several topics on different forums asking about similar problems but none of them seems to be the same issue.

Also seems like more empty browsers are open when I add more test to the suite. I'm not sure what of the following information will be relevant for this problem but I will try to provide a complete picture of what I have:

  • I'm using (as mentioned) TestNG - webdriver.
  • I'm working with page objects pattern.
  • I'm running my automation from a javafx interface, which allow the user to select some options.
  • I'm using ChromeDriver to emulate mobile devices.
  • I'm using a local thread of the driver which allow me to use an exclusive static driver for each test.
  • No empty browser are open when I don't run in parallel.

This is my TestNg.xml:

<suite name="Suite" parallel="classes" thread-count="4">
<test name="Test">
<groups>
     <run>
        <!-- Use include to specify what you want to run or exclude to not run the mentioned group(s) -->
        <include name="fullRegression"/>
        <exclude name="brokenOrDevelopment"/>
     </run>
  </groups>
<packages>
    <!-- Packages who contains the test cases classes -->
    <package name="Test"/>       
</packages>

This is the class which create the driver instance

public class TestCaseConfiguration {
 protected HomePageObjects homePage = null;
 protected DesiredCapabilities  capabilities;
 protected ExtentReports REPORTMANAGER;
 protected ExtentTest REPORT;

 public static ThreadLocal<ChromeDriver> driver;

 @BeforeMethod(alwaysRun = true)
 public void setup() throws InterruptedException {

     System.setProperty("webdriver.chrome.driver", "src/Tools/chromedriver.exe");//chrome driver version 2.20 | We need to have a relative path since no all the user will have the driver on C: 

     capabilities = DesiredCapabilities.chrome();
     Map<String, String> mobileEmulation = new HashMap<String, String>();
     mobileEmulation.put("deviceName", Constants.DEVICE);
     Map<String, Object> chromeOptions = new HashMap<String, Object>();
     chromeOptions.put("mobileEmulation", mobileEmulation);

     capabilities.setCapability(ChromeOptions.CAPABILITY, chromeOptions);

     /**
      * dirver is a ThreadLocal<ChromeDriver>. This allows the driver to be static and available for the complete project, and exclusive for each thread. 
      */
     driver=new ThreadLocal<ChromeDriver>()
                {
                    @Override
                    protected ChromeDriver initialValue()
                    {
                        return new ChromeDriver(capabilities); 
                    }
                };
     driver.get().manage().deleteAllCookies();
     driver.get().manage().timeouts().implicitlyWait(40, TimeUnit.SECONDS);
     driver.get().navigate().to(Constants.DOMAIN);

     homePage = PageFactory.initElements(driver.get(), HomePageObjects.class);
 }

 @AfterMethod(alwaysRun = true)
 public void close() throws InterruptedException {           
    driver.get().close();
    driver.get().quit();  
 }

}

This class is extended by each test case class. The tests run as expected, the only problem seems to be the extra browser instances.

Doesn't anyone know how can I avoid that issue or if I'm doing something wrong? Thanks for the help.

This is how the browser looks when open empty

回答1:

You are creating a new ThreadLocal<ChromeDriver> instance and reassigning driver each time setup runs. Imagine the following scenario where setup is called in parallel by multiple threads:

  1. Thread0 creates a ThreadLocal<ChromeDriver> instance, assigns it to driver, and calls driver.get(). One browser is now open.
  2. Thread1 creates a ThreadLocal<ChromeDriver> instance, assigns it to driver (replacing the one assigned to it by Thread0), and calls driver.get(). Two browsers are now open, one per ThreadLocal instance.
  3. Thread0 calls driver.get() again but driver no longer references the same ThreadLocal instance so a new browser is opened. Three browsers are now open, one in an "orphaned" ThreadLocal instance and two in the referenced ThreadLocal.

Now imagine that scenario but with more than two threads! You'll have lots of "orphaned" ThreadLocal instances floating around, some of which having a ChromeDriver instance associated with some thread(s) but no objects on any threads with references to the ThreadLocal in order to "get" them.

To fix the problem I recommend marking driver as final and assigning a ThreadLocal instance to it once.

e.g.

public class TestCaseConfiguration {
    protected HomePageObjects homePage = null;
    protected ExtentReports REPORTMANAGER;
    protected ExtentTest REPORT;

    public static final ThreadLocal<ChromeDriver> driver;
    private static final Set<ChromeDriver> drivers = Collections.newSetFromMap(new ConcurrentHashMap<>());
    static {
        System.setProperty("webdriver.chrome.driver", "src/Tools/chromedriver.exe");//chrome driver version 2.20 | We need to have a relative path since no all the user will have the driver on C:

        DesiredCapabilities capabilities = DesiredCapabilities.chrome();
        Map<String, String> mobileEmulation = new HashMap<String, String>();
        mobileEmulation.put("deviceName", Constants.DEVICE);
        Map<String, Object> chromeOptions = new HashMap<String, Object>();
        chromeOptions.put("mobileEmulation", mobileEmulation);

        capabilities.setCapability(ChromeOptions.CAPABILITY, chromeOptions);

        /**
         * dirver is a ThreadLocal<ChromeDriver>. This allows the driver to be static and available for the complete project, and exclusive for each thread.
         */
        driver = new ThreadLocal<ChromeDriver>() {
            @Override
            protected ChromeDriver initialValue() {
                ChromeDriver chromeDriver = new ChromeDriver(capabilities);
                drivers.add(chromeDriver);
                return chromeDriver;
            }
        };
    }

    @BeforeMethod(alwaysRun = true)
    public void setup() throws InterruptedException {
        driver.get().manage().deleteAllCookies();
        driver.get().manage().timeouts().implicitlyWait(40, TimeUnit.SECONDS);
        driver.get().navigate().to(Constants.DOMAIN);

        homePage = PageFactory.initElements(driver.get(), HomePageObjects.class);
    }

    @AfterSuite(alwaysRun = true)
    public void close() throws InterruptedException {
        for (ChromeDriver chromeDriver : drivers) {
            try {
                chromeDriver.quit();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}


回答2:

i created separate method in libFile which i call in every testcase.. @Libraryfile

//Variable declaration
WebDriver driver=null;

protected WebDriver getDriverInstance(){
   driver=new FirefoxDriver();
   return driver;
}

@testcase class - i am calling above method in @BeforeMethod //Variable declaration

WebDriver driver=null;

@BeforeMethod
public void setUp(){
 driver=getDriverInstance();

}

this has solved my Multiple empty browser instances