在 Specflow 中,我可以执行一次设置然后 运行 一堆独立的验证作为单独的测试吗?

In Specflow can I perform setup once then run a bunch of independent verifications as separate tests?

我正在为一组 API 编写自动化测试。我有 1 API 到 运行 一个搜索和另一个获取结果。我想 运行 搜索一次,然后进行多次测试 获取结果并 运行 验证。

RunOnce: 
    Given I have access to the User 'testUser'
    When I call POST /api/Search with
    """
    {
        "query": "legal",
        "snippetLength": 15,
        "resultSize": 4,
    ...
    }
    """

Scenario: SearchAPI - Verify Result Size
    When I call GET /api/Search
    Then The result size is X

Scenario: SearchAPI - Verify Filters
    When I call GET /api/Search
    Then The filters are X

Scenario: SearchAPI - Verify Sorting
    When I call GET /api/Search
    Then The sorting is X

Scenario: SearchAPI - Verify snippet
    When I call GET /api/Search
    Then The snippet length is X

这是一个非常糟糕的模式吗?由于时间问题,我不想 运行 搜索每个场景。但与此同时,我希望将测试分开,这样我就不会在单个场景中拥有所有断言以进行有效的结果分析。

我认为执行此操作的唯一方法是向功能添加标记并在 TestHooks 文件中实现它。但是如果 POST 调用失败,我就会失去很多可见性。

没有允许您执行此操作的 Gherkin 语言功能。相反,请考虑使用 FeatureContext 来存储搜索结果,因为 FeatureContext 对象由功能中的所有场景共享。您仍然会为提交搜索的给定步骤使用场景背景。步骤定义将检查特征上下文是否已经有搜索结果,如果是,则 Given 步骤将什么也不做。如果特征上下文中不存在搜索结果,则执行代码以POST进行搜索。

对搜索结果进行操作的任何“何时”或“然后”步骤都应从功能上下文中提取结果。

专题文件:

Background: 
    Given I have access to the User 'testUser'
    When I call POST /api/Search with
    """
    {
        "query": "legal",
        "snippetLength": 15,
        "resultSize": 4,
    ...
    }
    """

Scenario: SearchAPI - Verify Result Size
    When I call GET /api/Search
    Then The result size is X

Scenario: SearchAPI - Verify Filters
    When I call GET /api/Search
    Then The filters are X

Scenario: SearchAPI - Verify Sorting
    When I call GET /api/Search
    Then The sorting is X

Scenario: SearchAPI - Verify snippet
    When I call GET /api/Search
    Then The snippet length is X

接下来,使用FeatureContext存储和检索searchId,结果:

[Binding]
public class SearchSteps
{
    private readonly FeatureContext feature;

    /// <summary>
    /// Gets or sets the API search Id
    /// </summary>
    private string SearchId
    {
        get => (string)feature["foo"];
        set => feature["foo"] = value;
    }

    /// <summary>
    /// Gets or sets the API search results
    /// </summary>
    private IEnumerable<X> Results
    {
        get => (IEnumerable<X>)feature["searchResults"];
        set => feature["searchResults"] = value;
    }

    public SearchSteps(FeatureContext feature)
    {
        this.feature = feature;
    }

    [When(@"I call POST /api/Search with")]
    public void WhenICallPOST_api_searchWith(string json)
    {
        if (SearchId != null)
            return;

        var request = // parse json variable into object
        SearchId = api.PostSearch(request);
    }

    [When(@"I call GET /api/Search")]
    public void WhenICallGET_api_Search()
    {
        Results = api.GetSearch(SearchId);
    }

    [Then(@"The result size is (\d+)")]
    public void ThenResultSizeIs(int expectedResultSize)
    {
        Assert.AreEqual(Results.Count(), expectedResultSize);
    }

    // Other 'Then' steps make assertions on Results property
}

您可以神奇地访问 FeatureContext,因为 SpecFlow 已经使用依赖注入框架注册了该对象。只需将 FeatureContext 参数添加到步骤定义的构造函数中即可访问此对象。

我发现在 FeatureContext 对象上创建 get/set 值的私有属性很好,所以你只在一个地方投射对象。此外,它还为事物提供了一个有意义的名称,并使您在步骤定义中更容易处理 FeatureContext。

您可以将 IEnumerable<X> Results 替换为对 GET /api/Search/:searchId 的响应类型的名称。