如何编写相互跟随并共享数据的specflow场景?

How to write specflow scenarios that follow each other and share data?

TLDR - 编写相互遵循并共享数据的 specFlow 场景的最佳方式是什么?例如场景 A - 创建一个用户,然后场景 B 使用该用户登录(登录将需要在创建用户场景中指定的用户名和密码)。

大家好,

我正在尝试找出最佳方法来为网站上的典型注册用户表单编写一个场景,然后是一个测试登录的场景。

我已经尝试创建一个单独的 class 实例来存储我的注册表单的所有详细信息,当它到达此步骤时(认为这是上下文注入)。例如

[Binding]
public class AdmissionsPortalFeaturesSteps : BaseStep
{
    private UserDetails userDetails;

我遇到的问题是以下场景需要在前一个场景中生成的密码,我无法使用类似...

userDetails.Password 

这是我目前无法使用的,因为我想将密码值从第一个场景传递到第二个......(请注意该字段是否设置为 'random' 随机值在我的 C# 代码中生成)

Scenario: Create a new user via the Admissions Portal
    Given I will navigate to the Admissions Portal login page
    When I click the Create an Account link
    Then I will be on the ontrack: prospect - Register page
    Given I have entered the following values on the registration page
    | fieldName                  | value  |
    | Title                      | random |
    | Firstname                  | random |
    | Middlename                 | random |
    | Surname                    | random |
    | DOB                        | random |
    | Gender                     | random |
    | EmailAddress               | random |
    | MobileNumber               | 0      |
    | Password                   | random |
    | SecurityQuestion           | random |
    | SecurityAnswer             | random |
    And I click on the Register button
    Then I will be on the Login page
        And I will get a popup confirming my registration details
    Then I close the browser window

Scenario: I am able to login to the Admissions Portal with a newly created user
    Given I will navigate to the Admissions Portal login page
        And I enter a username and password and click to login
    Then I will be on the ontrack: prospect - Home page
    Then I close the browser window

目前我看到我有以下选项

  1. 扩展第一个场景以包括登录并使用上下文注入在步骤之间共享数据。
  2. 在我的登录步骤中重复包含 table 的注册步骤。
  3. 写一个新的更短的步骤来生成我的用户而不使用 table 并在两种情况下使用它(我可能想稍后指定在表单中设置特定值的场景,但承认我可能会更好放弃它)。

或者其他我没有想到的...

非常感谢,

我尝试确保我的 specflow 场景不共享任何数据,并且我是否可以在每次测试之间尽可能明确地重新生成整个数据库模式。

这确保数据库中只有您在测试中安排的数据

所以我会创建一个单独的 Given 步骤,它要么运行所有创建帐户的步骤(您可以通过编程方式调用这些步骤),要么有一个单独的 Given 步骤,使所有为数据库播种的信息短路你需要。

我还注意到您在测试中明确关闭了浏览器。我为 starts/stops Selenium 的网络创建了一个 BeforeScenario/AfterScenario 标签。这样您的测试只需要测试实际功能而不是 Selenium 的 starting/stopping。

public class SeleniumController
{
    public static readonly SeleniumController Instance = new SeleniumController();
    public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10);

    public IWebDriver Selenium { get; private set; }

    private void Trace(string message) { Console.WriteLine("-> {0}", message); }

    public void Start()
    {
        if (Selenium != null)
            return;

        string appUrl = ConfigurationManager.AppSettings["AppUrl"];

        var options = new ChromeOptions();
        options.AddArgument("test-type");
        Selenium = new ChromeDriver(options);
        Selenium.Manage().Timeouts().ImplicitlyWait(DefaultTimeout);

        Trace("Selenium started");
    }

    public void Stop()
    {
        if (Selenium == null) return;

        try
        {
            Selenium.Quit();
            Selenium.Dispose();
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex, "Selenium stop error");
        }
        Selenium = null;
        Trace("Selenium stopped");
    }
}

场景标签

public static class SeleniumSupport
{
    private static bool ReuseWebSession
    {
        get { return ConfigurationManager.AppSettings["ReuseWebSession"] == "true"; }
    }

    [BeforeScenario("web")]
    public static void BeforeWebScenario()
    {
        SeleniumController.Instance.Start();
    }

    [AfterScenario("web")]
    public static void AfterWebScenario()
    {
        if (!ReuseWebSession)
            SeleniumController.Instance.Stop();
    }
}

访问 selenium webdriver

的抽象步骤基础 class
public abstract class SeleniumStepsBase
{
    protected IWebDriver Selenium { get { return SeleniumController.Instance.Selenium; } }
}

我使用 FeatureContext.Current 或 ScenarioContext.Current 来存储我在场景或功能之间传递的信息。 FeatureContext 是一个 Specflow 全局字典,可以在任何地方访问。 Specflow FeatureContext

FeatureContext.Current["username"] = "someUsername";

或者

ScenarioContext.Current["username"] = "someUsername";

您打算使用一个场景为另一个场景设置数据是有缺陷的,恕我直言,注定要失败。

一个场景应该是完全独立的,并且能够运行 孤立,不依赖于另一个场景。 Specflow 是一个单元测试生成框架,但测试 运行 人员可以决定 运行 测试的顺序。如果你有一个你说你想要的依赖,那么如果它们不按顺序 运行 会发生什么?如果它们 运行 并行会怎样?试图使这项工作将逆流而上,不要这样做。

相反,让每个场景都自成体系。如果你有很多重复的设置,把它放在后台步骤中,或者创建一个调用所有其他步骤来进行设置的步骤,或者只创建一个只执行你想要的设置的步骤,然后使用标准方法在步骤之间共享数据以管理您生成的密码等