为什么始终对最小的代码单元进行单元测试是最佳实践?我发现那些测试永远不会在重构中存活下来

时间:2009-09-18 17:01:32

标签: unit-testing testing

多年来,我一直是测试驱动开发的实践者,总体而言我很满意。我还不了解的一个部分就是你应该总是对“最小单位”进行单元测试。

单元测试的部分想法似乎是允许你自信地重构你不会破坏任何东西。但是,我发现测试非常小的代码片段的测试几乎不会在这些重构中存活下来,代码总是会发生很大变化,以至于小单元测试会被丢弃并且新的测试被编写。这些测试涵盖了似乎在这里提供最大价值的更大功能,因为更高级别的接口不会经常更改。

对于微不足道的重构,比如移动方法,这些只是通过IDE完成的,因为我使用的是静态类型语言,所以我从来没有遇到IDE无法做到的情况。完美的重构。

其他人有相似或相反的经历吗?

6 个答案:

答案 0 :(得分:10)

我发现了同样的事情 - 但我认为区分的一件事是私有代码单元和公共可访问的代码单元。我认为始终对“公共API中公开的最小可用代码单元进行单元测试”非常重要。

重构期间公共API不应该更改(因为它会破坏二进制兼容性和版本控制),所以这个问题确实存在。

至于私有API,这里有一个平衡点。您测试的越小,您就越依赖测试。您的测试越高,测试越灵活,他们就越有可能在重构中存活下来。

话虽如此,我相信两者都很重要。大规模重构总是需要重新测试 - 这只是测试的一部分。

答案 1 :(得分:2)

这是一个粒度问题,比如Goldilocks和三只熊。你想要的东西不是太小,不是太大,而是恰到好处。

如果粒度太小,那么您可能发现这是浪费时间。如果它太大,那么它可能会错过重构约束,这些约束应该在重构/重新配置等方面保持不变。

与任何“最佳实践”一样,这些想法通常是在理论上发展出来的,但需要一些常识和针对您的特定情况进行定制,以便对您有用。

答案 2 :(得分:2)

在我看来,测试的代码单元越小,从测试失败中获得的信息就越多。如果你有一个覆盖更大代码的更高级别的测试,那么失败会告诉你更少的问题。

答案 3 :(得分:1)

大多数时候我只对公共类和方法进行单元测试。因为我认为,正如你所说,私人成员太不稳定,可能会发生变化。

对私有和内部成员的修改表明您更改了内部算法,而对公共成员的修改则表示语义修改。如果我认为更改私有成员会改变我的班级的语义,那么,也许这个成员不应该是私有的。

在重构类的内部算法时引入的错误在语义级别上打破了90%的时间测试,并且大多数时候,如果经常和早期测试,很快就会发现错误。

答案 4 :(得分:1)

这听起来并不像你做的那样Test-Driven Development,这需要迭代循环为一小段功能编写测试,创建满足测试的功能,然后重构以删除任何重复测试/代码可能已添加。听起来你正在测试事实(“代码总是变化得足够小,以至于小单元测试被扔掉了”)。如果测试是功能规范(就像在TDD中那样),那么重构永远不会导致测试“无法生存”。

因此,假设您没有真正使用TDD,那么您需要在编写测试代码的数量与开发生产代码的时间之间进行权衡。我会说,编写足够的测试代码,以便您知道您的代码完成它应该做的事情。如果你可以通过更粗粒度的测试来做到这一点,那很好,尽管正如其他人所说的那样,这使得更难以知道导致失败的原因。

测试不仅仅适用于重构。它知道你何时完成。因此,您可以放心地添加新功能,而不会破坏旧功能。在你很久以后,其他人可以进来了解你的代码,改变它,并确信它有效。

我建议您遵循Kent Beck所述的TDD练习。在事实之后编写测试比没有测试更好,但我发现这是一种比TDD更低效的实践。

答案 5 :(得分:1)

我一直在关注更多的BDD方法,我最终没有像结果一样测试功能。这样做时,您仍然在测试功能,但是通过预期结果来衡量。我发现这样做可以使你的测试更有意义,更不易碎,更适用,最后我写的更少。