在做TDD时,我为什么要“足够”才能通过测试?

时间:2010-08-20 20:02:08

标签: unit-testing tdd

查看this等帖子,看来正确的TDD方法是为一个功能编写一个测试,只需要传递该功能,然后根据需要添加另一个测试和重构。它通过,然后重复。

我的问题是:为什么使用这种方法?我完全理解写测试的第一个想法,因为它有助于你的设计。但是为什么我不能为特定函数创建所有测试,然后一次实现该函数直到所有测试都通过?

12 个答案:

答案 0 :(得分:11)

这种方法来自你不需要它的极限编程校长。如果您实际上编写了一个测试,然后是使其通过的代码然后重复该过程,您通常会发现您编写的内容足以使事情正常工作。您不会发明不需要的新功能。您不处理不存在的角落案例。

尝试一下实验。写出您认为需要的测试列表。把它放在一边。然后一次性进行一次测试。查看列表是否不同以及原因。当我这样做时,我几乎总是以较少的测试结束。我几乎总是发现我发明了一个我不需要的案例,如果我在第一时间进行所有测试。

答案 1 :(得分:4)

对我来说,这是关于“思想负担”。如果我立即担心所有可能的行为,我的大脑就会紧张。如果我一次接近一个,我可以充分注意解决当前的问题。

答案 2 :(得分:3)

我相信这源于“YAGNI”原则(“你不需要它”)(*),它表明类应该尽可能简单,没有额外的功能。因此,当您需要一个功能时,您可以为它编写测试,然后编写该功能,然后停止。如果您首先编写了一些测试,那么显然您只是在猜测将来某个时候您的API需要做什么。

(*)我通常将其翻译为“你太傻了,不知道将来需要什么”,但这是另一个话题......

答案 3 :(得分:3)

imho它减少了过度设计你正在编写的代码片段的机会。

当您查看不同的使用场景时,更容易添加不必要的代码。

答案 4 :(得分:2)

Dan North建议没有测试驱动设计这样的东西,因为测试并没有真正驱逐设计 - 这些单元测试只有在实现功能后才会成为测试,但在设计阶段你真的是以身作则。

这是有道理的 - 您的测试正在设置一系列样本数据和条件,待测系统将在这些数据和条件下运行,并根据这些示例场景推出设计。

其他一些答案表明这是基于YAGNI。这部分是正确的。

除此之外,还存在复杂性问题。正如经常所说,编程是关于管理复杂性 - 将事物分解为可理解的单元。

如果编写10个测试来覆盖param1为null的情况,则param2为null,string1为空,int1为负,并且当前星期为周末,然后去实现,你必须立刻兼顾了很多复杂性。这开辟了引入错误的空间,并且很难理清测试失败的原因。

另一方面,如果你编写第一个测试来覆盖空字符串1,你几乎不必考虑实现。测试通过后,您将转到当前周末的情况。你看一下现有的代码,逻辑应该去哪里显而易见。你运行测试,如果第一个测试现在失败,你知道你在实现星期几的事情时打破了它。我甚至建议您在测试之间提交源代码,这样如果您破坏某些内容,您可以随时恢复为通过状态并重试。

一次做一点,然后验证它是否能够显着减少引入缺陷的空间,并且在实施后测试失败时,您已经更改了很少的代码,以便很容易识别缺陷并纠正错误它,因为您知道现有代码已经正常运行。

答案 5 :(得分:1)

这是一个很好的问题。您需要在可能的测试和最可能的用户场景中编写所有测试之间找到平衡点。一个测试是,恕我直言,还不够,我通常喜欢写3或4个测试,代表该功能的最常见用途。我也想写一个最好的案例测试和最坏的案例测试。

编写许多测试可帮助您预测和了解功能的潜在用途。

答案 6 :(得分:1)

我相信TDD主张一次编写一个测试,因为它会迫使您根据在每一步中执行 最简单的事情 的原则来思考发展。

答案 7 :(得分:1)

我认为您发送的文章正是答案。如果您首先编写所有测试和所有场景,那么您可能会编写代码来同时处理所有这些场景,并且大多数情况下您最终可能会遇到处理所有这些问题的相当复杂的代码。 / p>

另一方面,如果你一次只进行一次,你最终每次都会重构你现有的代码,最终得到的代码可能就像所有场景一样简单。

就像你在问题中给出的链接一样,如果他们先写完所有的测试,我很确定他们最终会得到一个简单的if / else语句,但可能是一个相当复杂的递归片段代码。

答案 8 :(得分:1)

原则背后的原因很简单。坚持多么实际是一个单独的问题。

原因在于,如果您编写的代码超出了传递当前测试所需的内容,那么您编写的代码根据定义是未经测试的。 (这与YAGNI无关。)

如果您编写下一个测试以“赶上”生产代码,那么您刚刚编写了一个您没有看到过失败的测试。该测试可能被称为“TestNextFeature”,但它也可以return true用于您拥有的所有证据。

TDD是关于确保所有代码 - 生产和测试 - 都经过测试以及所有那些讨厌的“但我确信我写得对”错误不会进入代码。

答案 9 :(得分:0)

我会按照你的建议去做。为特定函数编写几个测试,实现该函数,并确保该函数的所有测试都通过。这样可以确保您与函数的实现分开理解函数的用途和用法。

答案 10 :(得分:0)

如果您需要比单元测试所测试的更多实现,那么您的单元测试可能不够全面。

我认为这个想法的一部分是保持简洁,保持设计/计划的功能,并确保您的测试足够。

答案 11 :(得分:0)

上面有很多好的答案 - YAGNI是第一个跳到脑海中的答案。

关于“刚刚获得测试通过”指南的另一个重要事项是,TDD实际上是一个三阶段过程:

红色>绿色>重构

经常重新审视最后一部分,即重构,是TDD的更多价值在于更清晰的代码,更好的API设计以及对软件的更多信心。你需要在很小的短块中重构,以免任务变得太大。

很难养成这种习惯,但要坚持下去,因为一旦你进入这个循环,这是一种奇怪的令人满意的工作方式。