Gherkin 使用一组特殊的 关键字 来为可执行规范提供结构和含义。每个关键字都被翻译成多种口语;在本参考中,我们将使用英语。

Gherkin 文档中的大多数行都以一个 关键字 开头。

注释只能放在新行的开头,特征文件中的任何位置。它们以零个或多个空格开头,后面跟着一个井号 (#) 和一些文本。

Gherkin 目前不支持块注释。

可以使用空格或制表符进行缩进。建议的缩进级别为两个空格。以下是一个示例

Feature: Guess the word

  # The first example has two steps
  Scenario: Maker starts a game
    When the Maker starts a game
    Then the Maker waits for a Breaker to join

  # The second example has three steps
  Scenario: Breaker joins a game
    Given the Maker has started a game with the word "silky"
    When the Breaker joins the Maker's game
    Then the Breaker must guess a word with 5 characters

每个步骤的尾部(关键字之后的部分)与一个称为 步骤定义 的代码块匹配。

请注意,有些关键字后面有冒号 (:),而有些则没有。如果您在不应该有冒号的关键字后面添加冒号,您的测试将被忽略。

关键字

每一行,除了空行,都必须以 Gherkin 关键字 开头,后面跟着任何您喜欢的文本。唯一的例外是放在 Example/ScenarioBackgroundScenario OutlineRule 行下的自由格式描述。

主要关键字是

还有一些次要关键字

  • """ (文档字符串)
  • | (数据表)
  • @ (标签)
  • # (注释)

本地化

Gherkin 已针对多种 口语 进行本地化;每种语言都有其自己的本地化关键字等效项。

Feature

Feature 关键字的目的是提供软件功能的高级描述,并对相关场景进行分组。

Gherkin 文档中的第一个主要关键字必须始终是 Feature,后面跟着一个 : 和描述该功能的简短文本。

您可以在 Feature 下面添加自由格式文本以添加更多描述。

这些描述行在运行时被 Cucumber 忽略,但可用于报告 (它们被像官方 HTML 格式化程序这样的报告工具包含在内)。

Feature: Guess the word

  The word guess game is a turn-based game for two players.
  The Maker makes a word for the Breaker to guess. The game
  is over when the Breaker guesses the Maker's word.

  Example: Maker starts a game

名称和可选描述对 Cucumber 没有特殊意义。它们的目的是为您提供一个地方来记录功能的重要方面,例如简要说明和业务规则列表 (一般验收标准)。

Feature 的自由格式描述在您开始使用 BackgroundRuleExampleScenario Outline (或它们的别名关键字) 关键字的行时结束。

您可以在 Feature 上方放置 标签 以对相关功能进行分组,而与文件和目录结构无关。

您只能在一个 .feature 文件中包含一个 Feature

描述

自由格式描述 (如上所述,用于 Feature) 也可以放在 Example/ScenarioBackgroundScenario OutlineRule 下面。

您可以编写任何您喜欢的内容,只要没有行以关键字开头即可。

描述可以采用 Markdown 格式 - 包括官方 HTML 格式化程序在内的格式化程序支持这一点。

Rule

(可选) Rule 关键字自 v6 起成为 Gherkin 的一部分。

Cucumber 对 Rule 的支持

Rule 关键字仍然很新。它已经在许多 Cucumber 实现中移植。但是,如果您遇到问题,请查看 Cucumber 实现的文档以确保它支持它。

Rule 关键字的目的是表示应实现的一项业务规则。它为功能提供了更多信息。Rule 用于对属于此业务规则的多个场景进行分组。Rule 应包含一个或多个阐明特定规则的场景。

例如

# -- FILE: features/gherkin.rule_example.feature
Feature: Highlander

  Rule: There can be only One

    Example: Only One -- More than one alive
      Given there are 3 ninjas
      And there are more than one ninja alive
      When 2 ninjas meet, they will fight
      Then one ninja dies (but not me)
      And there is one ninja less alive

    Example: Only One -- One alive
      Given there is only 1 ninja alive
      Then he (or she) will live forever ;-)

  Rule: There can be Two (in some cases)

    Example: Two -- Dead and Reborn as Phoenix
      ...

Example

这是一个具体示例,用于说明业务规则。它包含一个 步骤 列表。

关键字 ScenarioExample 关键字的同义词。

您可以添加任意数量的步骤,但建议每个示例有 3-5 步。如果步骤过多,示例将失去其作为规范和文档的表达能力。

除了作为规范和文档外,示例还是一个测试。总的来说,您的示例是系统的一个可执行规范

示例遵循以下模式

  • 描述初始上下文 (Given 步骤)
  • 描述事件 (When 步骤)
  • 描述预期结果 (Then 步骤)

步骤

每个步骤都以 GivenWhenThenAndBut 开头。

Cucumber 按您编写的顺序逐个执行场景中的每个步骤。当 Cucumber 尝试执行步骤时,它会查找匹配的步骤定义以执行。

在查找步骤定义时,不会考虑关键字。这意味着您不能有一个 GivenWhenThenAndBut 步骤与另一个步骤具有相同的文本。

Cucumber 认为以下步骤是重复的

Given there is money in my account
Then there is money in my account

这看起来可能是一个限制,但它迫使您想出一个更清晰、更明确的领域语言

Given my account has a balance of £430
Then my account should have a balance of £430

Given

Given 步骤用于描述系统的初始上下文 - 场景的场景。通常这是发生在过去的事情。

当 Cucumber 执行 Given 步骤时,它将配置系统以处于定义良好的状态,例如创建和配置对象或将数据添加到测试数据库。

Given 步骤的目的是在用户 (或外部系统) 开始与系统交互 (在 When 步骤中) 之前,将系统置于已知状态。避免在 Given 中谈论用户交互。如果您要创建用例,Given 将是您的先决条件。

可以有多个 Given 步骤 (使用 AndBut 用于 2 号及更高版本以使其更易读)。

示例

  • 米奇和米妮开始了一场游戏
  • 我已登录
  • 乔的余额为 42 英镑

When

When 步骤用于描述事件或操作。这可以是个人与系统交互,也可以是另一个系统触发的事件。

示例

  • 猜一个词
  • 邀请朋友
  • 取款

想象一下 1922 年

大多数软件都会做一些人们可以手动完成的事情 (只是效率不高)。

努力想出不依赖于技术或用户界面的示例。想象一下 1922 年,那时还没有计算机。

实现细节应隐藏在 步骤定义 中。

Then

Then 步骤用于描述预期的结果。

Then 步骤的 步骤定义 应使用断言来比较实际结果 (系统实际执行的操作) 与预期结果 (步骤中描述的系统应执行的操作)。

一个结果应该是可观察的输出。也就是说,从系统中输出的东西(报告、用户界面、消息),而不是系统内部深埋的行为(比如数据库中的记录)。

示例

  • 看到猜的词是错误的
  • 收到邀请
  • 卡片应该被吞下

虽然在数据库中实现Then步骤可能很诱人,但要抵制这种诱惑!

你应该只验证对用户(或外部系统)可观察的结果,而数据库的更改通常不可观察。

以及,但是

如果你有连续的GivenThen,你可以这样

Example: Multiple Givens
  Given one thing
  Given another thing
  Given yet another thing
  When I open my eyes
  Then I should see something
  Then I shouldn't see something else

或者,你可以通过将连续的GivenThen替换为AndBut,使示例的结构更流畅。

Example: Multiple Givens
  Given one thing
  And another thing
  And yet another thing
  When I open my eyes
  Then I should see something
  But I shouldn't see something else

*

Gherkin 还支持使用星号 (*) 来代替任何正常的步骤关键字。当你有一些实际上是事情清单的步骤时,这很有用,这样你就可以用更像项目符号的方式来表达它,否则And等的自然语言可能读起来不太优雅。

例如

Scenario: All done
  Given I am out shopping
  And I have eggs
  And I have milk
  And I have butter
  When I check my list
  Then I don't need anything

可以表达为

Scenario: All done
  Given I am out shopping
  * I have eggs
  * I have milk
  * I have butter
  When I check my list
  Then I don't need anything

背景

有时你会发现自己在Feature中所有场景中重复相同的Given步骤。

由于它在每个场景中都被重复,因此这表明这些步骤对描述场景来说不是必要的;它们是附带细节。你可以将这些Given步骤直接移到背景中,方法是在Background部分下将它们分组。

Background允许你为后面的场景添加一些上下文。它可以包含一个或多个Given步骤,这些步骤在每个场景之前执行,但在任何Before hooks之后执行。

Background放在第一个Scenario/Example之前,缩进级别相同。

例如

Feature: Multiple site support
  Only blog owners can post to a blog, except administrators,
  who can post to all blogs.

  Background:
    Given a global administrator named "Greg"
    And a blog named "Greg's anti-tax rants"
    And a customer named "Dr. Bill"
    And a blog named "Expensive Therapy" owned by "Dr. Bill"

  Scenario: Dr. Bill posts to his own blog
    Given I am logged in as Dr. Bill
    When I try to post to "Expensive Therapy"
    Then I should see "Your article was published."

  Scenario: Dr. Bill tries to post to somebody else's blog, and fails
    Given I am logged in as Dr. Bill
    When I try to post to "Greg's anti-tax rants"
    Then I should see "Hey! That's not your blog!"

  Scenario: Greg posts to a client's blog
    Given I am logged in as Greg
    When I try to post to "Expensive Therapy"
    Then I should see "Your article was published."

Background也支持在Rule级别,例如

Feature: Overdue tasks
  Let users know when tasks are overdue, even when using other
  features of the app

  Rule: Users are notified about overdue tasks on first use of the day
    Background:
      Given I have overdue tasks

    Example: First use of the day
      Given I last used the app yesterday
      When I use the app
      Then I am notified about overdue tasks

    Example: Already used today
      Given I last used the app earlier today
      When I use the app
      Then I am not notified about overdue tasks
  ...

你每个FeatureRule只能有一组Background步骤。如果你需要为不同的场景使用不同的Background步骤,请考虑将你的场景集分解为更多Rule或更多Feature

有关Background的更不明确的替代方法,请查看条件钩子.

使用背景的技巧

  • 不要使用Background来设置复杂的狀態,除非这种状态是客户实际上需要知道的。
    • 例如,如果用户和站点名称对客户来说无关紧要,请使用更高级别的步骤,例如Given I am logged in as a site owner
  • 保持Background部分简短
    • 客户在阅读场景时实际上需要记住这些内容。如果Background超过 4 行,请考虑将一些无关的细节移到更高级别的步骤中。
  • 使Background部分生动
    • 使用丰富多彩的名称,并尝试讲故事。人脑比记住像"User A""User B""Site 1"这样的名称更容易记住故事。
  • 保持场景简短,不要太多。
    • 如果Background部分已滚动出屏幕,则读者不再能全面了解正在发生的事情。考虑使用更高级别的步骤,或拆分*.feature文件。

场景大纲

Scenario Outline关键字可用于多次运行相同的Scenario,但使用不同的值组合。

关键字Scenario Template是关键字Scenario Outline的同义词。

复制和粘贴场景以使用不同的值很快就会变得单调乏味且重复。

Scenario: eat 5 out of 12
  Given there are 12 cucumbers
  When I eat 5 cucumbers
  Then I should have 7 cucumbers

Scenario: eat 5 out of 20
  Given there are 20 cucumbers
  When I eat 5 cucumbers
  Then I should have 15 cucumbers

我们可以将这两个类似的场景折叠成一个Scenario Outline

场景大纲允许我们通过使用带< >分隔的参数的模板来更简洁地表达这些场景。

Scenario Outline: eating
  Given there are <start> cucumbers
  When I eat <eat> cucumbers
  Then I should have <left> cucumbers

  Examples:
    | start | eat | left |
    |    12 |   5 |    7 |
    |    20 |   5 |   15 |

示例

Scenario Outline必须包含一个或多个Examples(或Scenarios)部分。它的步骤被解释为一个模板,该模板永远不会直接运行。相反,Scenario Outline在它下面的Examples部分中的每一行都运行一次(不包括第一行标题行)。

步骤可以使用<>分隔的参数,这些参数引用示例表中的标题。Cucumber 会用表中的值替换这些参数,然后它会尝试将步骤与步骤定义匹配。

你也可以在多行步骤参数中使用参数。

步骤参数

在某些情况下,你可能想要传递给步骤的数据比单行容纳的更多。为此,Gherkin 有Doc StringsData Tables

Doc Strings

Doc Strings对于将较大的文本块传递给步骤定义很有用。

文本应由三对双引号分隔符偏移,这些分隔符位于它们自己的行上。

Given a blog post named "Random" with Markdown body
  """
  Some Title, Eh?
  ===============
  Here is the first paragraph of my blog post. Lorem ipsum dolor sit amet,
  consectetur adipiscing elit.
  """

在步骤定义中,无需查找此文本并在模式中匹配它。它将自动作为步骤定义中的最后一个参数传递。

"""的缩进无关紧要,虽然常见的做法是从封闭步骤缩进两个空格。但是,三引号内部的缩进很重要。Doc String 的每一行都将根据打开的"""取消缩进。因此,超过打开的"""列的缩进将保留。

Doc Strings 也支持使用三个反引号作为分隔符。

Given a blog post named "Random" with Markdown body
  ```
  Some Title, Eh?
  ===============
  Here is the first paragraph of my blog post. Lorem ipsum dolor sit amet,
  consectetur adipiscing elit.
  ```

对于那些习惯使用 Markdown 编写的人来说,这可能很熟悉。

对反引号的支持

虽然所有当前版本的 Cucumber 都支持反引号作为分隔符,但许多工具(如文本编辑器)还不支持。

可以为 DocString 添加注释,说明它包含的内容类型。你可以在三引号后指定内容类型,如下所示。

Given a blog post named "Random" with Markdown body
  """markdown
  Some Title, Eh?
  ===============
  Here is the first paragraph of my blog post. Lorem ipsum dolor sit amet,
  consectetur adipiscing elit.
  """

对内容类型的支持

虽然所有当前版本的 Cucumber 都支持内容类型作为分隔符,但许多工具(如文本编辑器)还不支持。

数据表

Data Tables对于将值列表传递给步骤定义很有用。

Given the following users exist:
  | name   | email              | twitter         |
  | Aslak  | [email protected]  | @aslak_hellesoy |
  | Julien | [email protected] | @jbpros         |
  | Matt   | [email protected]   | @mattwynne      |

就像Doc Strings一样,Data Tables将作为最后一个参数传递给步骤定义。

表格单元格转义

如果你想在表格单元格中使用换行符,可以将其写为\n。如果你需要|作为单元格的一部分,可以将其转义为\|。最后,如果你需要\,可以将其转义为\\

数据表 API

Cucumber 提供了丰富的 API 用于在步骤定义中操作表格。有关更多详细信息,请参阅数据表 API 参考参考。

口语

你为 Gherkin 选择的语言应该与你的用户和领域专家在谈论该领域时使用的语言相同。应避免在两种语言之间进行翻译。

这就是为什么 Gherkin 被翻译成70 多种语言

这是一个用挪威语编写的 Gherkin 场景

# language: no
Funksjonalitet: Gjett et ord

  Eksempel: Ordmaker starter et spill
    Når Ordmaker starter et spill
    Så må Ordmaker vente på at Gjetter blir med

  Eksempel: Gjetter blir med
    Gitt at Ordmaker har startet et spill med ordet "bløtt"
    Når Gjetter blir med på Ordmakers spill
    Så må Gjetter gjette et ord på 5 bokstaver

特征文件第一行上的# language:标题告诉 Cucumber 使用什么口语——例如,法语为# language: fr。如果你省略此标题,Cucumber 将默认为英语 (en)。

一些 Cucumber 实现也允许你在配置中设置默认语言,这样你就不需要在每个文件中放置# language标题。

你可以帮助我们改进此文档。编辑此页面.