怎样写一个好的 Gherkin
在 BDD 中,应该具备什么功能?
我如何决定一个功能应该是什么?我应该在编写行为规范之前先定义一个功能,还是应该从行为开始,看看它们如何组合成功能
功能、场景和行为都是应该仔细定义的常见 BDD
[^ 1 ]术语:
- 行为
- 具有输入、操作和预期结果的操作。
- 场景
- 使用正式步骤和示例对行为进行的规范。
- 功能
- 通常涉及多种行为的所需产品功能。
不要试图过度思考“功能”的定义。有些特征很小,而另一些功能很大。功能与场景或行为之间的主要区别在于,功能是客户期望收到的。小功能可能只涵盖少数甚至一种行为,而大功能可能涵盖多种行为。
Gherkin 语言具有功能和场景部分。从这个意义上说,功能只是相关场景的集合。它们大致与术语的更一般含义一致。
也不要过度考虑敏捷的特性。一些团队将功能定义为用户故事的集合。其他团队说一个用户故事是一个功能。就 Gherkin 而言,不要假设一个用户故事必须只有一个功能文件和一个功能部分。一个用户故事可以有零到多个功能文件来涵盖其行为。做任何合适的事情。
功能应由客户需求决定。他们应该解决客户的问题。例如,也许客户需要一种更好的方式来通过他们的在线商店处理订单。这就是功能应该开始的地方——作为业务需求。行为应该自然而然地成为修饰和改进工作的一部分。因此,在大多数情况下,应先识别功能,然后再识别个体行为。
正确的行为
BDD 初学者犯的最大错误是在没有行为驱动的心态的情况下编写 Gherkin。他们经常编写功能文件,就好像他们正在编写“传统”过程驱动的功能测试一样:带有操作和预期结果的分步说明。 HP ALM、qTest、AccelaTest 和许多其他测试存储库工具以这种格式存储测试。这些过程驱动的测试通常是必要的,并在系统中跟踪涵盖多种行为的路径。因此,它们可能会不必要地长,这会延迟故障调查、增加维护成本并造成混乱。
例如,让我们考虑在 Google 上搜索熊猫图像的测试。下面是一个合理的测试程序:
- 打开网络浏览器。
- Web 浏览器成功打开。
- 导航到 https://www.google.com/。
- 网页加载成功,谷歌图片可见。
- 在搜索栏中输入“熊猫”。
- 与“熊猫”相关的链接显示在结果页面上。
- 单击结果页面顶部的“图像”链接。
- 与“熊猫”相关的图片显示在结果页面上。
不好的例子:
# BAD EXAMPLE! Do not copy.
Feature: Google Searching
Scenario: Google Image search shows pictures
Given the user opens a web browser
And the user navigates to "https://www.google.com/"
When the user enters "panda" into the search bar
Then links related to "panda" are shown on the results page
When the user clicks on the "Images" link at the top of the results page
Then images related to "panda" are shown on the results page
这个场景是非常错误的。所发生的一切只是作者在传统测试的每一步之前都放置了 BDD 流行语。这不是行为驱动的,它仍然是程序驱动的。
前两个步骤纯粹是设置:它们只是去谷歌,它们是非常必要的。由于它们不关注所需的行为,因此可以简化为一个声明性步骤:“假设网络浏览器位于 Google 主页上。”这个新步骤读起来更友好。
在 Given 步骤之后,有两个 When-Then 对。这在语法上是不正确的:Given-When-Then 步骤必须按顺序出现并且不能重复。 Given 可能不会跟在 When 或 Then 之后,而 When 可能不会跟在 Then 之后。原因很简单:任何单个 When-Then 对都表示一个单独的行为。这使得很容易看出,在上面的测试中,实际上涵盖了两种行为:(1) 从搜索栏搜索,以及 (2) 执行图像搜索。在 Gherkin 中,一种场景涵盖一种行为。因此,应该有两种情况而不是一种情况。任何时候您想要编写多个 When-Then 对,请改为编写单独的场景。 (注意:一些 BDD 框架可能允许无序的步骤,但它仍然是反行为的。)
这种拆分技术还揭示了不必要的行为覆盖。例如,从搜索栏搜索的第一个行为可能包含在另一个特征文件中。我曾经看到过一个包含大约 30 个 When-Then 对的场景,其中许多是重复的行为。
不要试图随意重新分配步骤类型以使场景遵循严格的 Given-When-Then 顺序。尊重步骤类型的完整性:Givens 设置初始状态,Whens 执行操作,然后验证结果。在上面的例子中,第一个 Then 步骤可能已经变成了一个 When 步骤,但这是不正确的,因为它做出了断言。步骤类型旨在成为编写良好行为场景的指南。
正确的例子:
Feature: Google Searching
Scenario: Search from the search bar
Given a web browser is at the Google home page
When the user enters "panda" into the search bar
Then links related to "panda" are shown on the results page
Scenario: Image search
Given Google search results for "panda" are shown
When the user clicks on the "Images" link at the top of the results page
Then images related to "panda" are shown on the results page
第二个行为可以说需要第一个行为先运行,因为第二个行为需要从搜索结果页面开始。但是,由于这只是为图像搜索行为设置而不是其中的一部分,因此第二个场景中的 Given 步骤基本上可以声明(声明性地)“panda”搜索必须已经完成。当然,这意味着“熊猫”搜索将在测试时冗余运行,但场景的分离保证了行为级的独立性。
BDD 的基本规则:一种场景,一种行为!
请记住,行为场景不仅仅是测试——它们还代表了需求和验收标准。好的 Gherkin 来自良好的行为。
(有关 BDD 的基本规则和每个场景的多个 When-Then 对的更深入信息,请参阅我的文章,Are Gherkin Scenarios with Multiple When-Then Pairs Okay?)
步骤解析
您如何编写步骤很重要。如果一个步骤写得不好,它就不能轻易被重用。值得庆幸的是,一些基本规则保持一致的措辞和最大的可重用性。
以第三人称的视角写下所有步骤。如果第一人称和第三人称步骤混合,场景就会变得混乱。我什至专门针对这一点专门写了一整篇博文:Gherkin Steps 应该使用第一人称还是第三人称? TL;DR:始终使用第三人称。
将步骤写成主谓动作短语。为简洁起见,可能很容易将词性排除在 step line 之外,尤其是在使用 Ands 和 Buts 时,但部分短语会使 step 模棱两可,更有可能被不正确地重用。例如,考虑以下示例:
# BAD EXAMPLE! Do not copy.
Feature: Google Searching
Scenario: Google search result page elements
Given the user navigates to the Google home page
When the user entered "panda" at the search bar
Then the results page shows links related to "panda"
And image links for "panda"
And video links for "panda"
最后两个 And 步骤缺少主谓短语格式。链接是否是主题,意味着它们执行某些操作?或者,它们是直接对象,意味着它们接收某些动作?它们是否应该出现在结果页面上?如果其他人为另一个也有图像和视频链接的页面编写了一个场景 - 他们可以重用这些步骤吗?没有明确主语和谓语的写作步骤不仅英语不好而且沟通不畅。
此外,对每种类型的步骤使用适当的时态和措辞。为简单起见,所有步骤类型都使用现在时。与其花时间回到中学英语课上,让我们用一个不好的例子来说明时态:
# BAD EXAMPLE! Do not copy.
Feature: Google Searching
Scenario: Simple Google search
Given the user navigates to the Google home page
When the user entered "panda" at the search bar
Then links related to "panda" will be shown on the results page
上面的 Given 步骤使用现在时,但其主题具有误导性。当它说“给定用户导航”时,它表示一个动作。行动意味着行为的锻炼。然而,给定步骤旨在建立初始状态,而不是锻炼行为。这似乎是一个微不足道的细微差别,但它可能会使功能文件作者感到困惑,他们可能无法判断一个步骤是 Given 还是 When。更好的措辞是,“鉴于显示了 Google 主页。”它为场景建立了一个起点。对适当的主语使用现在时来表示状态而不是动作。
上面的 When 步骤在说“用户输入”时使用过去时。这表明一个动作已经发生。但是,When 步骤应指示当前正在发生操作。另外,这里的过去时与其他步骤中使用的时态冲突。
上面的 Then 步骤在说“结果将显示出来”时使用将来时。将来时对于 Then 步骤来说似乎很实用,因为它表明了在采取当前行动后应该得到的结果。但是,将来时强化了程序驱动的方法,因为它将场景视为时间序列。另一方面,行为是产品或功能的现在时方面。因此,最好用现在时写出 Then 步骤。
更正后的示例如下所示:
Feature: Google Searching
Scenario: Simple Google search
Given the Google home page is displayed
When the user enters "panda" into the search bar
Then links related to "panda" are shown on the results page
请注意,所有步骤都是以第三人称编写的。阅读 Gherkin Steps 应该使用过去时、现在时还是将来时?了解更多。
好的标题
好的头衔和好的步骤一样重要。标题就像一个场景的面孔——它是人们阅读的第一件事。它必须用简洁的一行来传达行为是什么。标题通常也由自动化框架记录。在我的文章 Good Gherkin Scenario Titles 中给出了编写好的场景标题的具体指示。
选择,选择
初学者的另一个常见误解是认为 Gherkin 对条件或组合逻辑有一个“或”步骤。人们可能会认为 Gherkin 有“Or”,因为它有“And”,或者程序员可能希望将 Gherkin 视为一种结构化语言。但是,Gherkin 没有“Or”步骤。自动化时,每个步骤都按顺序执行。
下面是一个基于经典超级马里奥视频游戏的坏例子,展示了人们可能想要如何使用“或”:
# BAD EXAMPLE! Do not copy.
Feature: SNES Mario Controls
Scenario: Mario jumps
Given a level is started
When the player pushes the "A" button
Or the player pushes the "B" button
Then Mario jumps straight up
显然,作者的意图是说当玩家按下两个按钮中的任何一个时,马里奥应该跳跃。作者想要涵盖相同行为的多种变体。为了以正确的方式执行此操作,请使用场景大纲部分来涵盖相同行为的多个变体,如下所示:
Feature: SNES Mario Controls
Scenario Outline: Mario jumps
Given a level is started
When the player pushes the "<letter>" button
Then Mario jumps straight up
Examples: Buttons
| letter |
| A |
| B |
已知的未知
测试数据可能难以处理。有时,可能可以在系统中植入数据并编写测试来引用它,但其他时候可能不会。谷歌搜索就是最好的例子:结果列表会随着谷歌和互联网的变化而变化。要处理已知的未知,请防御性地编写场景,以便底层数据的更改不会导致测试运行失败。此外,要真正由行为驱动,请不要将数据视为测试数据,而是将其视为行为示例。
考虑上一篇文章中的以下示例:
Feature: Google Searching
Scenario: Simple Google search
Given a web browser is on the Google page
When the search phrase "panda" is entered
Then results for "panda" are shown
And the following related results are shown
| related |
| Panda Express |
| giant panda |
| panda videos |
此方案使用步骤表来明确命名应该出现在搜索中的结果。将执行表的步骤以迭代表条目并验证每个出现在结果列表中。但是,如果熊猫快递倒闭,不再在结果中排名靠前呢? (希望不会。)然后测试运行会失败,不是因为搜索功能被破坏,而是因为硬编码变体变得无效。最好编写一个步骤来更智能地验证每个返回的结果是否与搜索词组有某种关联,例如:“结果页面上会显示与‘panda’相关的链接。”步骤定义实现可以使用正则表达式解析来验证每个结果链接中是否存在“panda”。
Gherkin 的另一个不错的功能是步骤定义可以在不需要公开数据时隐藏自动化中的数据。步骤定义还可以将数据传递到自动化中的未来步骤。例如,考虑另一个谷歌搜索场景:
Feature: Google Searching
Scenario: Search result linking
Given Google search results for "panda" are shown
When the user clicks the first result link
Then the page for the chosen result link is displayed
请注意 When 步骤没有明确命名结果链接的值 - 它只是说单击第一个。第一个链接的价值可能会随着时间的推移而改变,但总会有第一个链接。 The Then 步骤必须了解所选链接的一些信息才能成功验证结果,但它可以简单地将其引用为“所选结果链接”。在幕后,在步骤定义中,When 步骤可以将所选链接的值存储在一个变量中,并将该变量向前传递给 Then 步骤。
处理测试数据
某些类型的测试数据应该直接在 Gherkin 中处理,而其他类型则不应该。请记住,BDD 是示例规范——场景应该描述它们涵盖的行为,并且写入 Gherkin 的任何数据都应该支持这种描述性质。阅读处理 BDD 中的测试数据以获取有关处理测试数据的全面信息。
少即是多
场景应该简短而甜蜜。我通常建议场景应该有一位数的步数 (<10)。冗长的场景很难理解,而且它们通常表明做法很糟糕。一个这样的问题是编写命令性步骤而不是声明性步骤。我之前已经谈到过这个话题,但我想在这里彻底解释一下。
命令式步骤说明了操作应该如何发生的机制。他们是非常程序驱动的。例如,请考虑以下何时输入 Google 搜索的步骤:
- When the user scrolls the mouse to the search bar
- And the user clicks the search bar
- And the user types the letter “p”
- And the user types the letter “a”
- And the user types the letter “n”
- And the user types the letter “d”
- And the user types the letter “a”
- And the user types the ENTER key
现在,行动的粒度可能看起来有点过分,但它说明了命令式步骤非常关注如何采取行动这一点。因此,他们通常需要许多步骤才能完全完成预期的行为。此外,预期行为并不总是像声明性步骤那样自我记录。
声明性步骤说明应该发生什么动作,但不提供有关如何发生的所有信息。它们是行为驱动的,因为它们在更高层次上表达行动。上面示例中的所有命令式步骤都可以写在一行中:“当用户在搜索栏中输入‘panda’时。”滚动和击键是隐含的,最终将由步骤定义中的自动化处理。在尝试减少步数时,问问自己是否可以更明确地编写您的步骤。
冗长场景的另一个原因是场景大纲滥用。场景大纲使得在他们的示例表中添加不必要的行和列变得非常容易。不必要的行会浪费测试执行时间。额外的列表示复杂性。两者都应该避免。以下是在面对超大场景大纲时要问自己的问题:
- 每行是否代表一个等价类的变体?
- 例如,除了搜索“panda”之外,搜索“elephant”并没有增加太多的测试值。
- 是否需要涵盖所有输入组合?
- 具有 M 个输入的 N 列每列生成 MN 种可能的组合。
- 考虑让每个输入只出现一次,而不管组合如何。
- 是否有任何列代表不同的行为?
- 如果在同一步骤中从未将列一起引用,则这可能是正确的。
- 如果是这样,请考虑按列拆分场景大纲。
- 特征文件阅读器是否需要明确知道所有数据?
- 考虑在步骤定义中隐藏一些数据。
- 一些数据可以从其他数据推导出来。
这些问题旨在进行健全性检查,而不是硬性规定。要点是场景大纲应该专注于一种行为并且只使用必要的变化。
风格和结构
虽然风格在代码审查期间经常处于次要地位,但它是区分优秀功能文件和优秀功能文件的一个因素。在一个真正由行为驱动的团队中,非技术利益相关者将像工程师一样依赖功能文件。良好的写作风格可以改善沟通,而良好的沟通技巧不仅仅是简历上的花言巧语。
以下是一些关于良好风格和结构的花絮:
- 专注于客户需求的功能。
- 每个功能文件限制一个功能。这使得查找功能变得容易。
- 限制每个功能的场景数量。没有人想要一千行的特征文件。一个好的衡量标准是每个功能有十几个场景。
- 将每个场景的步骤数限制在十以下。
- 限制每一步的字符长度。常见的限制是 80-120 个字符。
- 使用正确的拼写。
- 使用正确的语法。
- 将 Gherkin 关键字大写。
- 将标题中的第一个单词大写。
- 不要将步骤短语中的单词大写,除非它们是专有名词。
- 不要在步骤短语的末尾使用标点符号(特别是句号和逗号)。
- 在单词之间使用单个空格。
- 缩进每个部分标题下方的内容。
- 用两个空行分隔功能和场景。
- 用 1 个空行分隔示例表。
- 不要用空行分隔场景中的步骤。
- 均匀地间隔表分隔符管道(“|”)。
- 采用一组标准的标签名称。避免重复。
- 将所有标签名称以小写形式书写,并使用连字符(“-”)分隔单词。
- 限制标签名称的长度。
如果没有这些规则,你可能会得到这样的结果:
# BAD EXAMPLE! Do not copy.
Feature: Google Searching
@AUTOMATE @Automated @automation @Sprint32GoogleSearchFeature
Scenario outline: GOOGLE STUFF
Given a Web Browser is on the Google page,
when The seach phrase "<phrase>" Enter,
Then "<phrase>" shown.
and The relatedd results include "<related>".
Examples: animals
| phrase | related |
| panda | Panda Express |
| elephant | elephant Man |
不要这样做。它看起来很可怕。请为自己的职业感到自豪。虽然自动化代码的某些部分可能看起来很毛茸茸,但 Gherkin 文件应该看起来很优雅。
Gherkin 这些行为!
通过这些最佳实践,您可以像专业人士一样编写 Gherkin 功能文件。不要害怕尝试:没有人第一次就做的很完美。作为初学者,我违反了我在这篇文章中提出的许多指导方针,但我边学边学。如果您遇到困难,请不要放弃。永远记住黄金 Gherkin 规则和 BDD 的基本规则!
[^ 1 ]: Behavior-Driven Development