试图了解 Specflow 与单元测试框架和 BDD 的关系

Trying to understand Specflow relation to unit test frameworks and BDD

我正在研究像 SpecFlow 这样的 BDD 解决方案并浏览各种示例,我看到了对其他 TDD 框架的引用,例如我熟悉的 MsTest 和 NUnit。我了解 Specflow 和 BDD 提供的价值。我在某处读到 Specflow 和 BDD "wraps" 你的单元测试。那么,对于 Specflow,"step definitions" 的目的是否与 MsTest 或 Nunit 之类的目的相同,而这些其他框架只是使用选项而不是 Step 定义?

简而言之 "step definitions" 与 MsTest 或 Nunit 之类的东西的用途不同。其实我可以写很多,但是文字会太多。让我展示一些简单的例子: 这是一个简单的功能文件

Feature: BingSearchUi

Scenario: Search in Bing
    Given I open page 'http://www.bing.com'
    And I have entered 'visual studio' into the bing search field
    When I press search button on the bing page
    Then the result should contain 'www.visualstudio.com' on the bing page

这是有效的 specflow 场景,但它本身无能为力。要使其工作,您必须编写代码,而代码不仅使用 Nunit 或 MsTest。它们可能是步骤实施的一部分,但不是必需的。 例如。这是 Given I open page 'http://www.bing.com' 实现,它实际上使用了 Selenium,而不是任何测试框架:

[Given(@"I open page '(.*)'")]
 public void GivenIOpenPage(string ulr)
 {            
   driver.Navigate().GoToUrl(ulr);
 }

但为了让事情变得更复杂,Specflow 生成带有步骤的 .cs 文件,如果我使用 NUnit,则此文件是 NUnit 测试 class。这是文件的一小部分:

[NUnit.Framework.TestAttribute()]
        [NUnit.Framework.DescriptionAttribute("Search in Bing")]
        [NUnit.Framework.CategoryAttribute("Selenium")]
        public virtual void SearchInBing()
        {
            TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Search in Bing", new string[] {
                        "Selenium"});
#line 15
this.ScenarioSetup(scenarioInfo);
#line 16
 testRunner.Given("I open page \'http://www.bing.com\'", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
#line 17
 testRunner.And("I have entered \'visual studio\' into the bing search field", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
#line 18
 testRunner.When("I press search button on the bing page", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
#line 19
 testRunner.Then("the result should contain \'www.visualstudio.com\' on the bing page", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
#line hidden
            this.ScenarioCleanup();
        }

并且这个生成的文件实际上被测试运行程序调用。

我认为了解它如何工作的最简单方法 - 在 github 上找到任何使用 specflow 的 public 存储库并使用它。

如果您返回 the origins of BDD,您会发现第一个工具 - JBehave - 最初是 JUnit 的替代品。当时还没有注解,所以 JUnit 过去常常查找以 "test":

这个词开头的东西

testDatabase

当然,这是毫无意义的。所以 JBehave 做了同样的事情,但是以单词 "should":

开头

shouldPersistRecordsUsingHibernate

所以现在我们可以看看我们的 class 应该 做什么,并用简单的英语谈论它的行为。 "Behaviour" 比 "test" 更有用,在这里。

一段时间后,丹·诺斯和克里斯·马茨之间进行了几次对话,他们发现丹对 classes 所做的与克里斯(当时的分析师)所做的是一样的整个系统,因此 scenario-运行* 组件诞生了。 Dan 将 JBehave 移植到 RBehave,后者成为 RSpec 的场景运行器,成为 SpecFlow 和 Cucumber 以及所有其他的。当然,现在的 JUnit 使用注释,所以我们无论如何都可以用 "should" 这个词开始,而 NUnit 几乎就是 JUnit for .NET。

重点是您可以使用 BDD 描述任何级别的代码。我将 BDD 用于我的 class 级示例以及我的系统级场景。

对于 class 级别的示例,唯一的受众是技术人员。在 NUnit 或 JUnit 中 write comments about how the class behaves 就足够了。某些框架(如 MSpec 或 RSpec)有其他捕获这些描述的方法,但受众仍然是技术人员。它仍然是BDD。使用 class 级别的示例,我们模拟了依赖关系,以便我们一次只查看行为的一个方面。这有助于推动良好的设计,与 TDD 相同。

不过,对于系统级示例,还有其他受众;非技术利益相关者。他们中的大多数人都能很好地理解可读代码,因此您可以制作 DSL 并 still use NUnit here too. However, there's benefit to being able to capture natural language a little more directly, without having to worry about making it run. That's where SpecFlow and the like 发挥作用。

解析自然语言并将其与步骤匹配的开销不小。在单位 / class 级别上不值得这样做,在该级别上,行为由单一职责原则、封装和良好设计的其他方面分开。自然语言也不像代码那样容易重构。

所以我们为最外层保存了自然语言场景,我们只需要一些系统行为的例子。然后我们以端到端行为的几个示例、更多集成/模块/库级场景以及更多单元测试(或示例)结束。这通常称为 "testing pyramid".

例如,如果我们想要验证一个表单,有几个例子就足够了,说明我们的应用程序如何帮助用户填写错误时的表单。我们可能有几个集成测试来检查我们是否在客户端和服务器端都进行了验证(例如,如果这对我们很重要)。然后我们将围绕实际验证 classes 进行一系列单元测试,涵盖所有不同的可能性。

回答您在shorthand中的问题:

  • SpecFlow、Cucumber、JBehave 和其他自然语言框架通常仅用于系统级场景,通常针对 UI.
  • 进行自动化
  • 你不必为那些使用框架;可以使用 NUnit(或 JUnit 等)中的 DSL 来实现。
  • 在 class 级别使用自然语言框架是矫枉过正的。在这里使用 NUnit。
  • BDD 开发者通常在系统级别有一层场景 运行,下面有大量 class 级别的示例(单元测试)。这就是您所说的 "wrapping"。
  • 可能还有中间层。

*"scenario" 和 "example" 这两个词几乎是同义词,意思差不多。传统上我们使用 "scenario" 仅指端到端示例,"example" 或 "unit test" 在 class 级别使用。

不过,关于 BDD,一件最重要的事情 就是确保你 与某人讨论 行为的那些方面,即使如果它是回顾或橡皮鸭。 BDD 更多的是关于对话而不是关于工具。