Migrating to NUnit from SpecRunner - Parallels Execution with Selenium

I am migrating my test framework to NUnit from SpecRunner since SpecRunner is not longer maintained.

So far i am able to run the tests in parrallel by using the assembly code [assembly: Parallelizable(ParallelScope.Fixtures)]

However I am using Selenium to run my UI tests, currently when i run the tests only one browser instance in open (just testing on Chrome) for the two tests causing one of them to fail.

In SpecRunner I could specify the number of threads by specifying them in the default.srprofile file using the testThreadCount attribute.

<Execution stopAfterFailures="0" retryFor="Failing" testThreadCount="3" testSchedulingMode="Sequential" />

And that would open 3 instances of Chrome locally on my machine, and on the CI machine.

My question is : Can I do something similar in NUnit3 to run a chrome instance per thread?

UPDATE

GetWebDriver.cs

    public class GetWebDriver
    {
        private static TestContextModified context;

        public static string url;

        public static IWebDriver WebDriver(string browserName)
        {
            IWebDriver driver;
            context = TestContextModified.GetContextInstance();
            url = context.AppSetting["url"];
            var seleniumHubURL = context.AppSetting["seleniumGridServer"];

            switch (browserName)
            {
                case "IE":
                    try
                    {
                        if (context.AppSetting["REMOTE"] == "true")
                        {
                            InternetExplorerOptions options = new InternetExplorerOptions();

                            options.AddAdditionalCapability("ignoreProtectedModeSettings", true);

                            options.AddAdditionalCapability(CapabilityType.AcceptSslCertificates, true);

                            options.AddAdditionalCapability(CapabilityType.IsJavaScriptEnabled, true);

                            options.AddAdditionalCapability(CapabilityType.Platform,
                                new Platform(PlatformType.Windows));
                            driver = new RemoteWebDriver(
                                    new Uri(seleniumHubURL), options.ToCapabilities(), TimeSpan.FromSeconds(600))
                            { Url = url};
                        }
                        else
                        {
                            return new InternetExplorerDriver(new InternetExplorerOptions { IgnoreZoomLevel = true }) { Url = url};
                        }

                        return driver;
                    }
                    catch
                    {
                        string strCmdText;
                        strCmdText = @"/C cd bin\Debug&taskkill /im IEDriverServer.exe /f";
                        System.Diagnostics.Process.Start("CMD.exe", strCmdText);
                        Console.WriteLine("Deleted Chromedriver becouse Exception was raised");
                        return null;
                    }
                case "Chrome":
                    try
                    {
                        if (context.AppSetting["REMOTE"] == "true")
                        {
                            ChromeOptions options = new ChromeOptions();
                            options.AddArguments("--window-size=1920,1080");
                            options.AddArguments("--start-maximized");
                            options.AddArguments("--headless");
                            options.AddArgument("--disable-gpu");

                            driver = new RemoteWebDriver(
                                    new Uri(seleniumHubURL), options)
                            { Url = url};
                        }
                        else
                        {
                            return new ChromeDriver { Url = url};
                        }

                        return driver;
                    }
                    catch
                    {
                        string strCmdText;
                        strCmdText = @"/C cd bin\Debug&taskkill /im chromedriver.exe /f";
                        System.Diagnostics.Process.Start("CMD.exe", strCmdText);
                        Console.WriteLine("Deleted Chromedriver becouse Exception was raised");
                        return null;
                    }
                case "Firefox":
                    try
                    {
                        if (context.AppSetting["REMOTE"] == "true")
                        {
                            FirefoxOptions options = new FirefoxOptions();
                            driver = new RemoteWebDriver(
                                new Uri(seleniumHubURL), options);
                            return driver;
                        }
                        else
                        {
                            return new FirefoxDriver { Url = url};
                        }
                    }
                    catch
                    {
                        string strCmdText;
                        strCmdText = @"/C cd bin\Debug&taskkill /im geckodriver.exe /f";
                        System.Diagnostics.Process.Start("CMD.exe", strCmdText);
                        Console.WriteLine("Deleted Chromedriver becouse Exception was raised");
                        return null;
                    }

                case string browser: throw new NotSupportedException($"{browser} is not a supported browser");
                default: throw new NotSupportedException("not supported browser: <null>");
            }
        }
    }
}

IntiliazeWebdriver.cs

 public class IntializeWebdriver 

{
    public static IWebDriver driver;
    public IntializeWebdriver()
    {

    }
    public static IWebDriver webDriver()
    {
        if (driver == null)
        {
            driver = GetWebDriver.WebDriver("Chrome");
            return driver;
        }
        else
        {
            return driver;
        }
    }
}

And then in my steps:

  public class Steps: IntializeWebdriver
    {
        public Steps stepsTest;
        private ScenarioContext scenarioContext;
        public Steps(ScenarioContext _scenarioContext)
        {
            scenarioContext = _scenarioContext;
            stepsTest= new Step(webDriver());
        }
...

2 answers

  • answered 2022-03-01 11:08 Kumar Rishabh

    Looks like this line is causing issue :-

    public static IWebDriver driver;
    

    Can you try by not making this static? I remember that I faced issue in parallel execution via Nunit when the driver was static.

  • answered 2022-03-01 14:13 Greg Burghardt

    Kumar's answer leads you in the right direction. The web driver cannot be static, because then multiple threads (hence multiple tests) share the same instance of web driver. Instead, each scenario needs to create its own web driver instance and register it with the dependency injection framework. Unfortunately with how your code is currently, this will involve a good amount of refactoring. Despite this being a moderate amount of work, it is worth the effort.

    1. Initialize web driver in a [BeforeScenario] hook (see SpecFlow docs):

      [Binding]
      public class SpecFlowHooks
      {
          private readonly IObjectContainer container;
      
          public SpecFlowHooks(IObjectContainer container)
          {
              this.container = container;
          }
      
          [BeforeScenario]
          public void CreateWebDriver()
          {
              // Initialize whichever web driver you want to use, however
              // you want to initialize it
              var driver = new ChromeDriver(...);
      
              // Register the web driver with SpecFlow's dependency injection framework
              container.RegisterInstanceAs<IWebDriver>(driver);
          }
      }
      
    2. Refactor step definition classes to accept an IWebDriver object as a constructor parameter:

      [Binding]
      public class YouStepDefinitions
      {
          private readonly IWebDriver driver;
      
          public YouStepDefinitions(IWebDriver driver)
          {
              this.driver = driver;
          }
      
          [Given("...")]
          public void GivenX()
          {
              driver.FindElement(...)
          }
      }
      

    This ensures you initialize one web driver per scenario, which should allow your tests to run in parallel.

    At the end of the test, you can use an [AfterScenario] hook to destroy the driver and even take a screenshot:

    [Binding]
    public class SpecFlowHooks
    {
        private readonly IObjectContainer container;
    
        ...
    
        [AfterScenario]
        public void CreateWebDriver()
        {
            var driver = container.Resolve<IWebDriver>();
            var photographer = (ITakeScreenShot)driver;
    
            // take screenshot with 'photographer' variable
    
            driver.Quit();
            driver.Dispose();
        }
    }
    

How many English words
do you know?
Test your English vocabulary size, and measure
how many words do you know
Online Test
Powered by Examplum