C# 拥有多个Step文件将打开多个浏览器 问题是:

C# 拥有多个Step文件将打开多个浏览器 问题是:,c#,selenium,selenium-webdriver,bdd,specflow,C#,Selenium,Selenium Webdriver,Bdd,Specflow,如果我有多个Steps文件,那么当我执行测试时,似乎每个测试的WebDriver创建都在进行,而不管我运行哪个测试 每当我运行测试时,我都会看到一个看似随机的Chrome浏览器打开。为了查看SpecFlow和ChromeDriver之间是否存在某种不兼容(我知道这是一个很长的问题),我将搜索测试的WebDriver改为Firefox,并将登录测试的WebDriver改为Chrome。无论我运行什么测试,我总是看到2个浏览器打开;一个Chrome和一个Firefox 当我将所有步骤从SearchT

如果我有多个Steps文件,那么当我执行测试时,似乎每个测试的WebDriver创建都在进行,而不管我运行哪个测试

每当我运行测试时,我都会看到一个看似随机的Chrome浏览器打开。为了查看SpecFlow和ChromeDriver之间是否存在某种不兼容(我知道这是一个很长的问题),我将搜索测试的WebDriver改为Firefox,并将登录测试的WebDriver改为Chrome。无论我运行什么测试,我总是看到2个浏览器打开;一个Chrome和一个Firefox

当我将所有步骤从SearchTestSteps.cs文件移到LoginTestSteps.cs文件中时,问题就消失了

所以,是的,这解决了眼前的问题,但将我的所有步骤都放在一个文件中是次优的。这可能很快变得难以处理

因为每一组步骤都需要有自己的WebDriver,所以我不知所措

这可能与文件夹结构和存储位置有关吗?这是我的

Root
 |-Page Object Files
      |- Page Components
      |- Pages
      |- Test Tools  
 |- Step Definitions
      |- <*Steps.cs>  
 |- TESTS
      |- BDD Tests
          |-<*.feature>
      |- *standard selenium test files*

您可能已经在每个.cs文件中创建了驱动程序实例。 例如:在LoginSteps.cs中,您正在以下位置创建chrome驱动程序

private static readonly IWebDriver Driver = new ChromeDriver();

您应该在xStep.cs文件之外创建驱动程序,并将其传递给基于框架的类/方法。

我找到了如何使用绑定作用域解决此问题的方法

在每个步骤文件中,我可以执行以下操作:

  [BeforeFeature(), Scope(Feature = "SearchTests")]
  public static void Startup()
  {
      _driver = new ChromeDriver();
  }

  [AfterFeature()]
  public static void ShutDown()
  {
      _driver?.Close();
  }
这样做只会打开和关闭我想要的测试文件的驱动程序。
如果需要更细粒度,我还可以在每次测试之前选择标记的范围。

最终,这是由于在步骤定义类中将web驱动程序创建为静态字段造成的。你需要集中这个逻辑

您希望在创建功能之前创建web驱动程序,在SpecFlow依赖项注入容器中注册它,然后将该IWebDriver对象传递给步骤定义

我发现实现一个“懒惰”的web驱动程序是个好主意,因此浏览器窗口只有在您的C#代码实际需要与之交互时才会出现。这个
LazyWebDriver
类实现了IWebDriver接口,是一个真正web驱动程序的包装器

LazyWebDriver.cs

public sealed class LazyWebDriver : IWebDriver
{
    private readonly Lazy<IWebDriver> driver;

    public string Title => driver.Value.Title;

    // Other properties defined in IWebDriver just pass through to driver.Value.Property

    public LazyWebDriver(Func<IWebDriver> driverFactory)
    {
        driver = new Lazy<IWebDriver>(driverFactory);
    }

    public IWebElement FindElement(By by)
    {
        return driver.Value.FindElement(by);
    }

    public void Close()
    {
        driver.Value.Close();
    }

    // other methods defined in IWebDriver just pass through to driver.Value.Method(...)
}
[Binding]
public sealed class SeleniumHooks
{
    private readonly IObjectContainer objectContainer;

    public SeleniumHooks(IObjectContainer objectContainer)
    {
        this.objectContainer = objectContainer;
    }

    [BeforeFeature]
    public void RegisterWebDriver()
    {
        objectContainer.RegisterInstanceAs<IWebDriver>(new LazyWebDriver(CreateWebDriver));
    }

    private IWebDriver CreateWebDriver()
    {
        return new ChromeDriver();
    }

    [AfterFeature]
    public void DestroyWebDriver()
    {
        objectContainer.Resolve<IWebDriver>()?.Close();
    }
}
请注意,IWebDriver对象作为构造函数参数传入LoginSteps。SpecFlow附带了一个依赖注入框架,它足够聪明,可以将您在SeleniumHooks中注册的LazyWebDriver作为IWebDriver参数传递给LoginSteps构造函数


请确保将
\u loginPage
字段设置为实例字段,而不是静态字段。

我想这就是我对SpecFlow的新鲜之处。如何将驱动程序从小黄瓜传递到steps文件。似乎没有办法做到这一点。坦率地说,我对SpecFlow没有太多的经验,但您必须使用“场景前”或“挂钩”。很抱歉,我无法提供更多信息。检查此链接。但我建议使用“open”方法创建一个浏览器_steps.cs。在方法中使用,检查驱动程序是否为nil,然后根据传递给方法的浏览器或全局变量创建驱动程序实例。通过这种方式,您不必处理step.cs文件中的每个驱动程序启动和中断。这也是我不愿意这样做的原因。我已经创建了一个baseSteps类,该类的驱动程序创建有before和after特性方法。。。到目前为止,一切都很顺利。祝你一切顺利!
[Binding]
public sealed class SeleniumHooks
{
    private readonly IObjectContainer objectContainer;

    public SeleniumHooks(IObjectContainer objectContainer)
    {
        this.objectContainer = objectContainer;
    }

    [BeforeFeature]
    public void RegisterWebDriver()
    {
        objectContainer.RegisterInstanceAs<IWebDriver>(new LazyWebDriver(CreateWebDriver));
    }

    private IWebDriver CreateWebDriver()
    {
        return new ChromeDriver();
    }

    [AfterFeature]
    public void DestroyWebDriver()
    {
        objectContainer.Resolve<IWebDriver>()?.Close();
    }
}
[Binding]
public class LoginSteps
{
    private readonly IWebDriver Driver;
    private LoginPage _loginPage;

    private static string _username;
    private static string _password;
    private static string _repo;

    public LoginSteps(IWebDriver driver)
    {
        Driver = driver;
    }

    [Given(@"I am on the Login page")]
    public void GivenIAmOnTheLoginPage()
    {
        _loginPage = new LoginPage(Driver);
    }

    [Given(@"I have a good username/password combination")]
    public void GivenIHaveAGoodUsernamePasswordCombination()
    {
        _username = Nomenclature.WebClientPersonalUsername;
        _password = Nomenclature.WebClientPersonalPassword;
    }
    [Given(@"I select a repository")]
    public void GivenISelectARepository()
    {
        _repo = Nomenclature.RepoUnderTest;
    }

    [When(@"I fill out the form and submit")]
    public void WhenIFillOutTheFormAndSubmit()
    {
        _loginPage.Login(
            username: _username, 
            password: _password, 
            repo: _repo);
    }

    [Then(@"I am taken to the repo page")]
    public void ThenIAmTakenToTheRepoPage()
    {
        Assert.AreEqual(
            expected: _repo,
            actual: Driver.Title);

        HelperMethods.Logout(Driver);
    }
}