BDD Automation Framwork 自动化框架 文章有介绍好几种 BDD 框架,SpecFlow 就是其中一种,它源于 Cucumber,是基于 .NET 平台的 BDD 测试框架。本文将通过一个简单的例子来实践一把 SpecFlow BDD 测试,运用 SpecFlow 模板。


创建一个被测程序,也叫作 System Under Test (SUT),这个程序是一个简单的计算器,是一个 C# 类库。
打开 Visual Studio,创建一个新的 C# class library

输入项目名 SpecFlowCalculator 并选择一个目录保存


注意:项目名不要用任何特殊的符号,例如括号 (),将会导致 SpecFlow 生成的代码产生编译错误。
重命名 Class1.cs 成 Calculator.cs 并用下面代码覆盖,并编译成功
using System;
namespace SpecFlowCalculator
{
public class Calculator
{
public int FirstNumber { get; set; }
public int SecondNumber { get; set; }
public int Add()
{
throw new NotImplementedException();
}
}
}

将创建一个 SpecFlow 项目并添加到已经存在的 SpecFlow Calculator solution。
右键 “Solution ‘SpecFlowCalculator’,并选择 “Add ➡ New Project…” 菜单

搜索 “SpecFlow”, 选择 “SpecFlow Project” template 并点击 Next.

输入项目名 “SpecFlowCalculator.Specs”. 保留建议的地址并点击 Create

注意:项目名不要用任何特殊的符号,例如括号 (),将会导致 SpecFlow 生成的代码产生编译错误。
配置 Test Framework (Runner). 这里我们选择 xUnit, 当然你也可以选择自己偏好的 Runner

Visual Studio 创建了一个新的 SpecFlow 项目,并且在幕后解决了 NuGet 依赖包

添加被测程序的项目依赖
因为我们要测试 “Calculator” 类的实现,所以需要添加一个项目引用指向 “SpecFlowCalculator” class library 到新创建的 SpecFlow 项目中
右键 “Dependencies” 并选择 “Add Project Reference…” 菜单

在 “Reference Manager” dialog 选择 “SpecFlowCalculator” class library

Build 整个 Solution
现在这个 solution 已经创建起来了,一个 class library 包含 calculator 的实现,一个 SpecFlow 项目包括对 calculator 的实现。

通过菜单 “Tests ➡ Test Explorer” 打开 Test Explorer
看到测试用例已经添加到 SpecFlow 项目了,这是因为 SpecFlow 项目模板默认添加的。注意 Outcome 部分测试用例是 “Not Run”,因为还没有执行测试用例。

当我们运行所有的测试用例时,发现结果是失败的,很多 steps 被 Skip 了,是因为我们还没有实现这些 Scenario steps。

打开 Calculator.feature 文件 (SpecFlowCalculator.Specs ➡ Features ➡ Calculator.feature)

Feature 文件的目的就是以人类可读的并且可自动化测试的方式记录计算器期望的行为。SpecFlow 用 Gherkin 语言来用 Given/When/Then steps 描述 Scenarios,具体可参考 BDD - Gherkin Language,当前只有一个 Scenario(SpecFlow 项目模板自动添加的),用来描述两个数字相加,计算器应该正常工作。
基于 Scenario 文本,SpecFlow 生成一个自动化测试来执行这个 Scenario,只是目前还没有定义 Scenario steps 实际应该做什么。
右键第一个Given step “Given the first number is 50” 并选择 “Go To Definition”

Visual Studio 会定位到该 Step 绑定的 step definition (binding) 处. 它会打开 CalculatorStepDefinitions 类文件,并跳转到 GivenTheFirstNumberIs 方法.

step definition 被定位到,基于 类的 [Binding] 属性 和 方法的 [Given] 属性。Given 属性的正则表达式正好匹配 feature 文件中 Scenario 的 step。
添加下面字段到 step class 来实例化 calculator 类,这是我们前面创建的 SUT 需要测试的程序。
private readonly Calculator _calculator = new Calculator();
[Given("the first number is (.*)")]
public void GivenTheFirstNumberIs(int number)
{
_calculator.FirstNumber = number;
}


[Given("the second number is (.*)")]
public void GivenTheSecondNumberIs(int number)
{
_calculator.SecondNumber = number;
}

我们在 Gherkin Scenario 中用 “And” 关键字是为了可读性更好。 “And” 关键字会根据前面的 steps 被解析成 “Given”, “When” or “Then”。因为第一个 step 是一个 “Given” step,所以第二个 step “And the second number is 70” 会被解析成一个 “Given” step。
When 属性, 因为在 feature 文件中,该 step 是属于 “When” step.private int _result;
[When("the two numbers are added")]
public void WhenTheTwoNumbersAreAdded()
{
_result = _calculator.Add();
}

这个实现是调用 calculator 的 Add 方法,注意这个 Add 方法的结果没有存储在 calculator 的字段或属性中。一个好的实践就是将方法返回值存储 step definition 类的一个字段中,方便后续的重用。
Then 属性, 因为在 Scenario 中该 step 是一个 “Then” step.using FluentAssertions;
利用 FluentAssertions 类库,验证 Add 方法的结果是否匹配期望的值。
[Then("the result should be (.*)")]
public void ThenTheResultShouldBe(int result)
{
_result.Should().Be(result);
}

实现了所有的 step definitions,代码如下:
using FluentAssertions;
using TechTalk.SpecFlow;
namespace SpecFlowCalculator.Specs.Steps
{
[Binding]
public sealed class CalculatorStepDefinitions
{
// For additional details on SpecFlow step definitions see https://go.specflow.org/doc-stepdef
private readonly ScenarioContext _scenarioContext;
private readonly Calculator _calculator = new Calculator();
private int _result;
public CalculatorStepDefinitions(ScenarioContext scenarioContext)
{
_scenarioContext = scenarioContext;
}
[Given("the first number is (.*)")]
public void GivenTheFirstNumberIs(int number)
{
_calculator.FirstNumber = number;
}
[Given("the second number is (.*)")]
public void GivenTheSecondNumberIs(int number)
{
_calculator.SecondNumber = number;
}
[When("the two numbers are added")]
public void WhenTheTwoNumbersAreAdded()
{
_result = _calculator.Add();
}
[Then("the result should be (.*)")]
public void ThenTheResultShouldBe(int result)
{
_result.Should().Be(result);
}
}
}

public int Add()
{
return FirstNumber + SecondNumber;
}


为了方便共享测试结果,我们需要生成一个测试报告。
打开 一个 commnd 命令

安装 LivingDoc CLI 作为一个全局的 dotnet tool
dotnet tool install --global SpecFlow.Plus.LivingDoc.CLI

cd C:\Work\SpecFlowCalculator\SpecFlowCalculator.Specs\bin\Debug\net6.0
livingdoc test-assembly SpecFlowCalculator.Specs.dll -t TestExecution.json
有必要请重现打开 Command 命令执行

C:\Work\SpecFlowCalculator\SpecFlowCalculator.Specs\bin\Debug\net6.0\LivingDoc.html
注意,如果你的 JSON 文件名字是 FeatureData.JSON,而不是 TestExecution.JSON,这表明你的 CLI tool 是老版本的,最好升级到最新版本。
查看 calculator feature 的执行文档,可以看到 Scenarios 和 steps 的执行结果。

点击 “Analytics” tab 来查看 test result summary :

有关更多 SpecFlow+LivingDoc 信息,请参考 SpecFlow+ LivingDoc’s documentation
我们实践了 SpecFlow 的模板项目,从 Scenaro steps 的绑定实现,运行测试,到生成测试报告整个过程,现在你也可以根据自己的项目需要开展 SpecFlow BDD 测试了,创建自己项目的 Feature 文件,设计 Gherkin Senario Steps,并用代码实现对应的 step definition.