5

我一直在尝试在 Ruby 中学习 Cucumber,我认为最好的方法是制作我自己的项目。但是,我想知道什么是好的“给定”条款。

据我了解,“Given”基本上是一个设置,“When”是被测函数,“Then”是预期结果。

例如,假设我正在制作一个基于实体踏入熔岩的 Minecraft 场景。我当前的 GWT 如下所示:

Scenario: Take damage when I stand in lava.
  Given an entity is standing next to a block of lava with 10 health
  When the entity steps in the block of lava
  Then the entity should take 2 damage

然而,这个“给定”步骤似乎相当“关闭”。我必须站在熔岩块旁边才能使这种情况起作用,这是没有意义的。类似地 - 我将如何为应该总是发生的场景编写(和测试)GWT - 例如,我如何确保只要我的实体留在熔岩中,它就会继续受到伤害?我发现很难编写代码来测试一个实体在熔岩中停留了多长时间。系统如何知道实体在熔岩中呆了多长时间?在我看来,测试这类事情几乎需要我写下世界其他地方,以便能够说“这个实体已经在熔岩中呆了 x 秒,推进模拟,我损失了多少马力”

想法?

4

4 回答 4

2

你不必重写世界。你只需要能够欺骗你关于世界状态的测试(在这种情况下,时间)。在测试中控制时间的常用方法是存根。

我会像这样写那个场景

Scenario: Take damage when I stand in lava.
  Given I have 10 health
  And there is a block of lava next to me
  When I note the time
  And I step in to the block of lava
  And I wait 5 seconds
  Then I should have 8 health

并实现这样的时间步骤:

When /^I note the time$/ do
  @start = Time.now
end

When /^I wait (\d+) seconds$/ do
  Time.stub(:now) { @start + 5.seconds }
end

When I note the time有点人为,所以如果有意义的话,你可以把它折叠到另一个步骤。(在这种情况下,我看不到合适的步骤,但您可能会在更长的情况下。)When I wait 5 seconds但是完全适合域。

其他细节:

  • 使用第一人称是简洁且适合该领域的。
  • Given适用于场景开始之前为真的条件。一种思考方式是,从Given变为真到实际场景开始之间可能已经过去了一段时间,在此期间可能发生了与场景无关的其他事情。
  • 健康与站在熔岩旁边没有太大关系,因此最好分步设置。
  • 测试你受到了两次伤害(而不是你的健康是 8)将需要断言步骤和Given初始化你的健康之间的依赖关系。最小化这种依赖使 Cucumber 步骤更可重用。因此,只要它不损害可理解性(我认为在这种情况下不会这样做),只需在最后断言最终状态即可。
于 2014-06-09T16:38:04.533 回答
1

有趣的问题!

“我必须站在一块熔岩旁边才能让这个场景起作用,这没有任何意义。”

如果实体没有站在熔岩旁边,那么它将无法进入熔岩。你到底不喜欢你的场景?

现在关于测试对实体造成多少伤害,如果您正在编写这个场景来测试实际的 Minecraft 游戏,那么您必须启用某种基于浏览器的计时器来计算监视器经过的时间量(如果它正在浏览器中播放)。这确实会很尴尬。

但是,如果您正在编写自己的 Minecraft 版本,那么您可以编写场景以便它测试代码本身(即不测试在浏览器中运行的代码)。例如:

Scenario: Take damage when I stand in lava.
  Given an entity is standing next to a block of lava with 10 health
  When the entity steps in the block of lava
  And remains there for a unit of time
  Then the entity should take 2 damage

如果此测试正在执行您编写的代码,您将能够准确控制实体在熔岩区域中花费的时间量(因此使用“时间单位”)

相似地:

Scenario: Take fatal damage when I remain standing in lava.
  Given an entity is standing next to a block of lava with 10 health
  When the entity steps in the block of lava
  And remains there for 5 units of time
  Then the entity should lose all health

当你说:

“在我看来,测试这类事情几乎需要我写下世界其他地方”

当你说“几乎”时,你一针见血。这种 BDD 方法的关键是采用一种渐进的方法,并在最初尽可能多地模拟以满足测试。一旦测试通过,然后使用 TDD 实现模拟区域。

于 2014-01-31T00:54:14.117 回答
0

如上所述,Give 子句表示某种设置。我的大部分 Given 条款都可以在我的背景中找到。例如:

  Feature: A New User 
  Background:
    Given I am a new user

  Scenario: Writing a new Feature 
    And I add "text" to my new feature
    Then I should have a new feature named "feature.feature"

在这种情况下,后台会验证“我是新用户”。在功能文件中,后台步骤在每个后续场景之前运行。

于 2014-01-31T22:43:43.860 回答
0

我试图提高 Fresh 答案的可读性。我目前正在学习小黄瓜(使用优秀的黄瓜书)。代码可以从minecraft_gherkin_example下载

Feature: Take damage when I stand in lava.

In minecraft, lava is bad for your health. 
    Every unit of time, damage reduces your health.

Scenario Outline: Step into lava
    Given my health level is <Health>
    When I step into the lava
        And I wait <LavaTime> unit of time
    Then my new health level is <NewHealth>
        And the outcome is <Outcome>

Examples:
  | Health | LavaTime |  NewHealth | Outcome   |
  |  10    |    1     |     8      | Alive     |
  |  10    |    4     |     2      | Alive     |
  |  4     |    1     |     2      | Alive     |
  |  2     |    1     |     0      | Game over |
于 2014-06-09T13:57:02.343 回答