从 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 驱动程序实例并将其注册到依赖注入框架。不幸的是,根据您当前的代码,这将涉及大量重构。尽管这是一个适度的工作量,但值得付出努力。
在 [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);
}
}
重构步骤定义 类 以接受 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();
}
}
我正在将我的测试框架从 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 驱动程序实例并将其注册到依赖注入框架。不幸的是,根据您当前的代码,这将涉及大量重构。尽管这是一个适度的工作量,但值得付出努力。
在
[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); } }
重构步骤定义 类 以接受
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();
}
}