0

I have automated regression tests that run every morning. Currently, it launches dozens of threads simultaneously, each running its own webdriver in each thread.

                ChromeOptions option = new ChromeOptions();
                option.AddArgument("--headless");
                IWebDriver driver = new ChromeDriver(option);                    

                try
                {
                    SuiteDriver(driver, suiteTable);
                    LogMonitor.UEErrorHandling();
                }
                catch (Exception ex)
                {
                    WritetoLogFile("Exception in Main - " + ex);
                }
                finally
                {
                    workbook.Dispose();
                    driver.Quit();
                }

When the tests complete there are a bunch of webdriver instances still running. When I attempt to clean these up at the end of the test run using driver.Quit() it closes more than just the driver in its own thread, causing the other tests to fail to complete. Driver.Quit() doesn't seem to differentiate between the driver launched by this one instance and other drivers launched by other instances of the test.

Is there a way to ensure driver.Quit() or driver.Close() only closes the instance of webdriver launched by that specific executable running in that thread only?

Appu Mistri
  • 768
  • 8
  • 26
HeadlyvonNoggin
  • 313
  • 1
  • 3
  • 14
  • You will have to use ThreadLocal instances for each thread. While quiting the driver, you will have to use thread specific driver. There are plenty of examples on this. – Sureshmani Kalirajan Aug 01 '19 at 16:39
  • explanation: even though each thread has its own Chrome instance, they are all sharing the same chromedriver executable that is driving the browser... when you call quit(), it stops the chromedriver process and all subsequent requests from other threads can no longer communicate with the browser. – – Corey Goldberg Aug 02 '19 at 14:27

2 Answers2

0

Below is an example of thread-safe execution in C# using System.Threading.ThreadLocal

ThreadLocal<IWebDriver> DriversThread = new ThreadLocal<IWebDriver>();

IWebDriver Driver
{
    set => DriversThread.Value = value;
    get => DriversThread.Value;
}

string directoryPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); // Pass this directory path to chromedriver when you are installing chromedriver through Nuget packages.
ChromeOptions chromeOpt = new ChromeOptions();
chromeOpt.AddArguments("--headless");
Driver = new ChromeDriver(directoryPath, chromeOpt);

Driver.Url = "https://google.co.in";

Driver.Quit();

This will set thread specific drivers, each thread will have its own driver no matter how many threads are being executed.

Appu Mistri
  • 768
  • 8
  • 26
  • I appreciate the help, Appu. The above line where you first instantiate the IWebDriver is the only way I've found to do so with ThreadLocal. But I cannot find a way to specify ChromeDriver and the above code doesn't work for me. – HeadlyvonNoggin Aug 01 '19 at 21:40
  • I have done this by identifying the pid's and closing those out. I tried a quick search here and saw this thread - https://stackoverflow.com/questions/5901679/kill-process-tree-programmatically-in-c-sharp. See if this might help. – Dazed Aug 01 '19 at 23:34
  • @headlyvonnoggin what's the issue that you are facing when specifying ChromeDriver? I've edited my answer to add chrome options and path to the driver executable (you have to install the chromedriver through nuget packages). Let me know if this helps. – Appu Mistri Aug 02 '19 at 03:28
0

Try and give each webdriver it's own profile, and if needed download path.

private ChromeOptions GetChromeOptions()
{
    var options = new ChromeOptions();

    ProfilePath = Path.Combine(AppContext.BaseDirectory, "tmp", Guid.NewGuid().ToString());
    DownloadPath = Path.Combine(ProfilePath, "Downloads");
    if (!Directory.Exists(DownloadPath))
    {
        Directory.CreateDirectory(DownloadPath);
    }
    options.AddUserProfilePreference("download.default_directory", DownloadPath);
    //--lang=en-US,en headless does not define a language by default 
    options.AddArguments("--incognito", "--lang=en-US,en", $@"--user-data-dir={ProfilePath}");
    return options;
}

I'm using xUnit to spawn dozens of headless chromes tests and I've not ever seen the dispose close all of the instances. The only difference I can think of is that I am spawning each with their own profile.

I would suggest using the setup and teardown of your testing framework onto a base class to handle the setup and teardown for all tests.

public class PageLoad: IDisposable
{
    private IWebDriver _driver;

    public void PageLoad()
    {
        _driver = new ChromeDriver();
    }

    [Fact]
    public void PageLoadTest()
    {
        _driver.Navigate().GoToUrl("http://google.com");
        Assert.True(_driver.FindElement(By.XPath("//*[@id='hplogo']")).Displayed);
    }

    public void Dispose()
    {
        _driver.Dispose();
    }
}

You can also wrap the driver in a using statement

using (var driver = new ChromeDriver())
{
    driver.Navigate().GoToUrl("http://google.com");
    Assert.True(_driver.FindElement(By.XPath("//*[@id='hplogo']")).Displayed);
}
J.D. Cain
  • 639
  • 4
  • 16