尝试使用 TDD 编写有意义的测试时何时停止?

When to stop when trying to write meaningful tests using TDD?

我正在努力寻找何时停止使用 TDD 编写测试用例。

假设要写一个只接受一些字符串的方法,它只能接受字符串['red', 'green', 'blue'],它是必需的,不能为空。

我编写了第一个失败的测试,将其设为绿色,依此类推,直到我拥有测试用例:

it('should accept red input', () => { /*...*/ }
it('should accept green input', () => { /*...*/ }
it('should accept blue input', () => { /*...*/ }
it('should not accept null input', () => { /*...*/ }
it('should not accept empty input', () => { /*...*/ }

在这一点上一切都过去了,现在我应该结束它,还是应该去添加一个测试,如果它失败了 Purple?添加这个测试有意义吗? 如果是,我还能说出其他10种颜色来测试,我是否也应该考虑它们?

这个例子很简单,但在某些情况下,正则表达式有无限组合,我知道这可能是个问题,而且由于时间限制,我无法添加我能想到的所有测试用例。这些是最糟糕的,因为我不知道什么时候停止编写测试代码,并且发现什么时候足够了。

我知道我无法为此找到具体的答案,但我想从经验中听到大部分时间有效的方法。

此处 post 是一个很好的答案: Is there such a thing as excessive unit testing?

简而言之,您考虑失败的可能性并权衡编写自动化测试用例的成本。

在你的情况下,你可以测试另一种颜色,看看它是否正确拒绝它。但是针对所有情况进行测试是不必要且不可能的。

如果您开始收到某个值的重复错误,您可能希望将其添加到测试中。否则边界检查将执行(空或 None,正确的行为,一次失败)

通常您想要测试输入 类 而不是特定输入,除非您已经知道特定输入会产生您需要通过测试涵盖的特定情况。

在您的示例中,我会将其分解为四个测试:

  1. 应该接受预期输入中的颜色 space,例如'red'
  2. 不应接受不在预期输入中的颜色 space,例如'purple'
  3. 不应接受空输入
  4. 不应接受空输入

你是对的,解析输入 space 是巨大的,涵盖的案例比你实际测试的要多。在那种情况下,我会针对一些常见情况(null/empty 输入、明显预期和意外的输入等),并尝试考虑输入可能被错误分类为 (un-) 的特定场景预期的。 可能有两种颜色 'red' 和 'reddish',您想要测试该函数是否涵盖两者或其中一种,但不涵盖另一种。在这种情况下,具体的颜色并不重要,只是一个包含另一个。

At this point everything is passing, now should I call it a day and go or should I go and add a test if it fails for Purple? Is it meaningful to add this test? If it is, I still can name other 10 colours to test, should I consider them all too?

在不列举整个世界的情况下改进对测试对象的评估的一种方法是基于 属性 的测试。 Scott Wlaschin 写了一篇 good introduction to the technique.

Kent Beck 以提请注意测试和实施之间的数据重复而闻名,J. B. Rainsberger 建议,作为“删除重复”工作的一部分,数据往往会移向测试。那么这是什么意思呢?

在你的情况下,听起来你可以将测试对象分成两部分——一部分看起来像颜色名称的数据库(地图,查找 table),另一部分是一些给定颜色名称数据库做一些有趣的事情的逻辑。

对于后一部分,您最终会进行测试,例如“给定 [red, green, blue] 的数据库,当输入为紫色时,测试对象是否采用非颜色路径;给定[red, green, blue, purple] 的数据库,当输入为紫色时,测试对象是否采用那是一种颜色的路径。

这让您需要担心数据库。你要注意的是,简单地测试你在两个不同的地方输入了相同的数据并不是特别有价值。

您也可以考虑来自 Beck

的观察结果

I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence

记住,投入测试的时间是没有投入到其他地方的时间。有时正确的做法是发布,然后在您了解有关代码应该如何工作的新知识后,再回到代码。