问题
I've seen several approaches to instantiate web driver in Specflow examples.
- Creating it in the steps definition class and disposing it in
Dispose
method for the class
Why is it horrible? Cause 1 scenario doesn't equal 1 steps definition class as some steps are just shared between features and there will be more than 1 web driver instantiated. Example: https://www.softwaretestinghelp.com/specflow-and-selenium/
- Creating it in hooks
[BeforeScenario]
and destroying int in[AfterScenario]
It won't work with parallel execution(according to author). https://github.com/AutomateThePlanet/AutomateThePlanet-Learning-Series/tree/master/Specflow-Series/ExtendTestExecutionWorkflowUsingHooks
Question: How to manage WebDriver
instances in Specflow UI tests solution with NUnit? Where and when initialize it, where and when destroy and how to access it in page object models and steps definition classes?
回答1:
You need to use the dependency injection framework that comes with SpecFlow. Option #2 where you create it in the [BeforeScenario]
and destroy it in the [AfterScenario]
is the right way to do, but then you need to register the IWebDriver
object with the dependency injection framework:
[Binding]
public class WebDriverHooks
{
private readonly IObjectContainer container;
public WebDriverHooks(IObjectContainer container)
{
this.container = container;
}
[BeforeScenario]
public void CreateWebDriver()
{
FirefoxDriver driver = new FirefoxDriver();
// Make 'driver' available for DI
container.RegisterInstanceAs<IWebDriver>(driver);
}
[AfterScenario]
public void DestroyWebDriver()
{
var driver = container.Resolve<IWebDriver>();
if (driver != null)
{
driver.Quit();
driver.Dispose();
}
}
}
Your step definitions need to accept an IWebDriver object as a constructor argument. You could even register your page objects with the DI framework too.
[Binding]
public class LoginSteps
{
private readonly IWebDriver driver;
private readonly LoginPage loginPage;
public LoginSteps(IWebDriver driver)
{
// Assign 'driver' to private field or use it to initialize a page object
this.driver = driver;
// Initialize Selenium page object
this.loginPage = new LoginPage(driver);
}
[When(@"I go to the login page")]
public void WhenIGoToTheLoginPage()
{
// Use 'driver' in step definition
driver.FindElement(By.LinkText("Sign In")).Click();
}
[When(@"I log in")]
public void WhenILogIn()
{
// Use Selenium page object in step definition
loginPage.LogIn("testUser", "testPassword");
}
}
In the GitHub project you referenced, the web driver object is initialized in as a static property. This is the reason why that code example cannot be used for parallel tests. It sounds like all executing scenarios are using the same AppDomain, so they share static class state, which means each scenario is attempting to use the same browser instance.
来源:https://stackoverflow.com/questions/58390535/how-to-properly-manage-and-access-webdriver-instances-to-avoid-problems-with-par