如何拆解一个SpecFlow场景

13

我正在尝试为我正在工作的遗留网站创建一组新的测试。该网站在后端使用数据库。我打算使用SpecFlow和Selenium,但我还不确定如何最好地处理数据清理。

目前,我有一个带有样本数据集的数据库备份,每次测试运行之前都会还原该备份。然而,这很麻烦,因此我想只对发布前的关键测试运行执行此操作,并在其中间保留连续集成运行使用相同的数据库。

目前,我有大量类似于以下内容的测试:

Secenario: Test Item Creation
    Given I am logged in
    When I create an item with a unique name
    Then an item exists with the unique name

“when” 步骤使用 GUID 来确保名称是唯一的,然后步骤通过模块变量访问它以检查其是否存在。

就像我之前说的那样,我有很多类似这样的测试,并且在同一个数据库上多次运行它们,因此测试系统会充斥着这些项目,从而减慢搜索等操作的速度。

我的问题是应该如何处理这种情况?我是否应该创建另一个测试步骤来删除这个项目:

Secenario: Test Item Creation
    Given I am logged in
    When I create an item with a unique name
    Then an item exists with the unique name
    Then delete the item with the unique name

或者我的测试框架应该如何处理这个问题?如果是这样,人们会怎么做?考虑到SpecFlow步骤的全局性质,如果有多个具有父子关系的项目,则想要按正确顺序获取拆卸步骤可能会变得棘手。

1个回答

14

一份好的测试应该没有依赖关系,因此让测试步骤创建并“拆除”测试数据是一个好主意。

你可以采用的方法之一是存储由以下步骤生成的唯一名称:

When I create an item with a unique name

例如,在ScenarioContext对象中。

ScenarioContext.Current["testItem"] = "testItemName";

这将允许您在方案的持续时间内保留此值。

然后,您可以创建一个与特定标签相关联的SpecFlow钩子,以在方案完成时拆除此数据,例如(请注意标签):

@deleteTestItem
Secenario: Test Item Creation
Given I am logged in
When I create an item with a unique name
Then an item has exists with the unique name

清理代码的方法如下:

[AfterScenario("deleteTestItem")]
public void DeleteTestItem()
{
 var testItemName = ScenarioContext.Current["testItemName"];

 // Use testItemName to clean-up your database
}

然后,这段代码将仅用于具有此标签的测试场景。请注意,如果您的所有测试都涉及创建测试项,则可以省略标签并简单地使用 AfterScenario 钩子。

或者,您可以将所有测试项名称保存在 FeatureContext 中,然后在 AfterFeature 钩子中删除这些项。这将减少数据库调用次数(即您不会在每个场景后调用数据库进行清理)。

我更喜欢 ScenarioContext 方法,因为我认为,如果场景创建数据,则该场景应负责自行清理。


1
我更喜欢通过上下文注入的方式,通过步骤类的构造函数传递显式的上下文对象,并在那里填充它,而不是使用ScenarioContext.Current(在Specflow 2.0中无法用于并行测试),但我觉得这基本上是正确的方法。 - Sam Holder
@SamHolder ScenarioContext.Current是一个显式上下文对象,即它仅限于该场景实例。并行测试会带来哪些问题? - Ben Smith
是的,我知道ScenarioContext.Current是什么,但我认为这不是在Specflow中在步骤之间共享状态的最佳方式。基本上有3种方法,使用特定状态的显式类而不是使用通用的ScenarioContext要好得多。在给定的示例中,我可能会创建一个ItemContext类,并将其属性Name设置为Guid。然后,您可以以类型安全的方式使用此类,而不是依赖于基于字符串(和强制转换)的ScenarioContext.Current方法。 - Sam Holder
您可以在此处查看有关新并行测试支持的说明文档(https://github.com/techtalk/SpecFlow/wiki/Parallel-Execution)。我认为它解释了在这种新范式中使用“ScenarioContext.Current”存在的问题以及建议的解决方案(即注入ScenarioContext实例),但是在我看来,如果您要这样做,最好创建一个具有强类型优势的特定上下文对象。 - Sam Holder
请仅返回翻译后的文本:而且,当我说“显式上下文”时,我并不是指特定于该场景,而是指特定于特定任务(例如,在这种情况下保留对已创建项目 ID 的引用)。但是很好的答案,我只是指出了其他选项,因为我认为使用“ScenarioContext”不是一个好的做法(我希望它在2.0中消失,但是嘿!) - Sam Holder
嗨,Sam,是的,我非常清楚你知道ScenarioContext.Current是什么;D 我会查看那些链接。虽然使用强类型显式上下文对象很好,但在这种情况下,由于OP只处理单个字符串,我认为使用ScenarioContext已经足够了,并且是Specflow的当前教科书方法。如果必须保存更多数据,则我会提倡使用显式上下文对象。至于并行和ScenarioContext,我不知道当前实现中存在的问题。这归结于Martin是否并行运行这些测试。 - Ben Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接