Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
847 views
in Technique[技术] by (71.8m points)

.net - How to properly manage and access webdriver instances to avoid problems with parallel execution of tests?

I've seen several approaches to instantiate web driver in Specflow examples.

  1. 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/

  1. 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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...