18

希望我能清楚地解释我的问题,让其他人理解,我们开始吧,假设我有以下两个假设场景:

Scenario: Filter sweets by king size and nut content
Given I am on the "Sweet/List" Page
When I filter sweets by 
    | Field               | Value  |
    | Filter.KingSize     | True   |
    | Filter.ContainsNuts | False  |
Then I should see :
    | Value            |
    | Yorkie King Size |
    | Mars King Size   |

Scenario: Filter sweets by make
Given I am on the "Sweet/List" Page
When I filter sweets by 
    | Field        | Value  |
    | Filter.Make  | Haribo |
Then I should see :
    | Value   |
    | Starmix |

这些场景很有用,因为我可以根据需要添加任意多的字段/值行和然后值条目,而无需更改相关的已编译测试步骤。然而,不同过滤器测试的复制/粘贴场景将变得重复并占用大量代码 - 我想避免这种情况。理想情况下,我想创建一个场景大纲并保持上面测试的动态特性,但是当我尝试这样做时,我遇到了定义示例表的问题,我无法添加我认为合适的新行,因为那将是一个新的测试实例,目前我有这个:

Scenario Outline: Filter Sweets 
Given I am on the <page> Page
When I filter chocolates by 
    | Field    | Value   |
    | <filter> | <value> |
Then I should see :
    | Output   |
    | <output> |
Examples:
    | page       | filter      | value  | output  |
    | Sweet/List | Filter.Make | Haribo | Starmix |

所以我遇到了在使用场景大纲时能够动态地将行添加到我的过滤器和预期数据的问题,有人知道解决这个问题的方法吗?我应该从不同的角度来解决这个问题吗?

解决方法可能类似于:

Then I should see :
    | Output |
    | <x>    |
    | <y>    |
    | <z>    |
    Examples:
    | x | y | z |

但这不是很动态....希望有更好的解决方案?:)

4

2 回答 2

36

我认为 SpecFlow、Gherkin 和开箱即用的 Cucumber 无法满足您的要求。我不能代表作者说话,但我敢打赌,它故意不打算以这种方式使用,因为它违背了编写和实施这些规范的整体“流程”。在许多事情中,规范旨在为非程序员阅读,为程序员提供实现与规范匹配的代码的指南,用于集成测试,并在重构时提供一定的灵活性。

我认为这是一种情况,你感觉到的疼痛表明存在问题,但可能不是你想的那样。你说:

“但是,不同过滤器测试的复制/粘贴场景将变得重复并占用大量代码——我想避免这种情况。”

首先,我不同意以书面形式解释自己是“重复的”,至少与使用“the、apple、car 等”这样的特定词的重复性不同。一遍又一遍地。问题是:这些话是否正确地解释了你在做什么?如果是这样,并且解释您的情况需要您写出多个场景,那么这正是它所需要的。交流需要文字,有时甚至是相同的文字。

实际上,您所说的“重复性”是使用 Gherkin 和 Cucumber 或 SpecFlow 之类的工具的好处之一。如果您能够一遍又一遍地使用该句子,这意味着您不必一遍又一遍地编写测试代码。

其次,你确定你正在为正确的事情编写规范吗?我问只是因为如果场景的数量失控,以至于你有太多以至于人类无法理解你写的东西,那么你的规范可能没有针对正确的事情。

一个可能的例子是您在这种情况下如何测试过滤分页。是的,您希望您的规范涵盖全部功能,并且您的网站将在与您的过滤相同的页面上进行分页,但成本是多少?需要经验和实践才能知道什么时候放弃所谓的“理想”,即无嘲笑、完全集成的测试会产生更好的结果。

第三,不要认为规范意味着完美覆盖所有可能的场景。这些场景基本上是状态的快照,这意味着有些功能可以覆盖无限大的场景集,这是不可能的。所以你会怎么做?尽可能写出能够讲述故事的功能。甚至让故事驱动发展。但是,没有转化为您的规格或其他情况的细节最好留给直接 TDD,在规格之外完成。

在您的示例中,您似乎基本上是在讲述一个关于一个网站的故事,该网站允许用户创建针对糖果和糖果的动态搜索。他们输入一大组可能的搜索条件中的一个,单击一个按钮,然后获得结果。坚持下去,只写足够的规格来完成这个故事。如果您对覆盖范围不满意,请使用更多规范或单元测试对其进行清理。


无论如何,这只是我的想法,希望对您有所帮助。

于 2011-02-27T03:11:06.323 回答
8

从技术上讲,我认为您可以尝试从步骤定义中调用步骤:

从步骤定义调用步骤

例如,我认为您可以重写

Then I should see :
| Output   |
| <output> |

成为一个自定义步骤

I should have output that contains <output>

其中 output 是一个逗号分隔的期望值列表。在自定义步骤中,您可以将逗号分隔的列表分解为一个数组并对其进行迭代调用

Then "I should see #{iterated_value}"

您可以使用类似的技术来传递过滤器列表和过滤器值。您的特大号测试示例行可能看起来像

| page       | filter                               | value       | output                           |
| Sweet/List | Filter.KingSize, Filter.ContainsNuts | True, False | Yorkie King Size, Mars King Size |

或者可能

| page       |                              filter-value-pairs | output                           |
| Sweet/List | Filter.KingSize:True, Filter.ContainsNuts:False | Yorkie King Size, Mars King Size |

话虽如此,你或许应该把达伦的话牢记在心。我不确定这种方法是否有助于实现非开发人员可读的场景的最终目标。

于 2011-02-27T04:27:17.313 回答