从 SpecRunner 迁移到 NUnit - 使用 Selenium 的并行执行

Migrating to NUnit from SpecRunner - Parallels Execution with Selenium

我正在将我的测试框架从 SpecRunner 迁移到 NUnit,因为不再维护 SpecRunner。

到目前为止,我可以使用汇编代码 运行 并行测试 [assembly: Parallelizable(ParallelScope.Fixtures)]

但是我正在使用 Selenium 运行 我的 UI 测试,目前我 运行 只测试一个打开的浏览器实例(仅在 Chrome 上测试)对于导致其中一个失败的两个测试。

在 SpecRunner 中,我可以使用 testThreadCount 属性在 default.srprofile 文件中指定线程数。

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

这将在我的机器和 CI 机器上本地打开 3 个 Chrome 实例。

我的问题是:我可以在 NUnit3 中做一些类似于 运行 每个线程一个 chrome 实例的事情吗?

更新

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;
        }
    }
}

然后在我的步骤中:

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

看起来这一行引起了问题:-

public static IWebDriver driver;

你能不能试试不把它设为静态?我记得当驱动程序是静态的时候,我在通过 Nunit 并行执行时遇到了问题。

Kumar 的回答为您指明了正确的方向。 Web 驱动程序不能是静态的,因为多个线程(因此多个测试)共享同一个 Web 驱动程序实例。相反,每个场景都需要创建自己的 Web 驱动程序实例并将其注册到依赖注入框架。不幸的是,根据您当前的代码,这将涉及大量重构。尽管这是一个适度的工作量,但值得付出努力。

  1. [BeforeScenario] 挂钩中初始化 Web 驱动程序(参见 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. 重构步骤定义 类 以接受 IWebDriver 对象作为构造函数参数:

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

这确保您为每个场景初始化一个 Web 驱动程序,这应该允许您的测试 运行 并行。

在测试结束时,你可以使用一个[AfterScenario] hook来破坏驱动甚至截图:

[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();
    }
}