测试用例设计中的契诃夫之枪原则
看到一篇跟用例数据准备相关的文章,觉得挺有道理的。我之前在设计用例数据的时候就会犯类似的错误,这篇文章其实说的非常在理。
翻译了一下原文,供大家参考。
编写测试用例更像是讲故事而非纯技术工作,这种观点并不罕见。最近我在 The Bike Shed 播客中听到这个观点,而且在博客文章和会议演讲中也经常能看到类似的讨论。既然测试编写是一种讲故事的艺术,那么我们是否应该借鉴叙事原则来改进我们的测试呢?
谈到讲故事的原则,首先想到的就是契诃夫之枪原则。这个原则是什么?用安东·契诃夫自己的话说(引自大英百科全书):
如果在舞台上放置了一支上膛的步枪,那么它就必须被开火。不要做出你不打算履行的承诺。
大英百科全书还给出了如下定义:
这是一条适用于戏剧、文学和其他叙事形式的原则,它强调故事中引入的每个元素都应该对情节发展必不可少。
那么这个原则如何应用到我们的测试编写中呢?想象一下你正在为电商系统编写测试用例。你有不同类别的产品,这些类别对买家有一些限制条件。最明显的例子就是不能向未满十八岁的人销售酒类产品。在我们的例子中,这类人群不能将这些产品添加到购物车。
让我们看一个测试代码示例(我使用的是 Elixir,但这个原则适用于大多数编程语言):
test "don't allow adding products to cart when age constraint is not met" do
buyer = %Person{
name: "John Smith",
age: 17,
country: :uk,
registered_on: ~U[2023-09-16T18:17:22Z]
}
category = %Category{
name: "Alcohol",
external_id: 3242,
constraints: [
%AgeConstraint{min: 18}
]
}
product = %Product{
name: "Triple Hazy IPA",
category: category,
sku: "TRI-557",
added_at: ~U[2022-01-01T12:16:54Z]
}
cart = Cart.init(buyer)
assert Cart.add(cart, product, quantity: 2) == {:error, :constraint_violated}
end
这个测试本身并不算糟糕,但它在多处违反了契诃夫之枪原则。在我们测试的"准备"阶段引入的每个标量都是契诃夫意义上的"枪"。它们都被放在了舞台上,读者可能会期待它们都会"开火"。这里我们有 10 个标量,相当于舞台上放了 11 把枪。什么是"开火"?就是当我们把这个值改成其他值时,测试应该失败。让我们检查一下这些标量:
name: "John Smith"
: 无论改成什么,测试都不会失败age: 17
: 如果改成 18 或 22,测试会失败country: :uk
: 不会失败(除非我们实现了基于国家的限制,但目前没有)registered_on: <date>
: 无关紧要name: "Alcohol"
: 无关紧要external_id: 3242
: 这是什么?无关紧要min: 18
: 改成 15 会导致测试失败- 产品中剩余的 name、sku 和添加日期都不会影响测试
quantity: 2
: 同样无关紧要
总结一下,我们的 11 个"枪"中只有两个会"开火",约 18%。其余的都是纯粹的干扰,如果读者试图理解测试的动态性,这些都会让他们误入歧途。