Unit testing 为什么总是对尽可能最小的代码单元进行单元测试是最佳实践?我发现这些测试将永远无法通过重构

Unit testing 为什么总是对尽可能最小的代码单元进行单元测试是最佳实践?我发现这些测试将永远无法通过重构,unit-testing,testing,Unit Testing,Testing,我从事测试驱动开发已经好几年了,总的来说,我对它很满意。我还不明白的一点是,您应该始终对“可能最小的单元”进行单元测试 单元测试的部分理念似乎是让您能够满怀信心地重构,确保不会破坏任何东西。然而,我发现测试非常小的代码片段的测试几乎永远不会在这些重构中幸存下来,代码总是变化很大,以至于小的单元测试被扔掉,新的测试被编写出来。由于更高级别的接口不会经常更改,因此覆盖更大功能的测试似乎在这里提供了最大的价值 对于一些琐碎的重构,比如移动方法,这些都是通过IDE完成的,因为我使用的是静态类型的语言,所

我从事测试驱动开发已经好几年了,总的来说,我对它很满意。我还不明白的一点是,您应该始终对“可能最小的单元”进行单元测试

单元测试的部分理念似乎是让您能够满怀信心地重构,确保不会破坏任何东西。然而,我发现测试非常小的代码片段的测试几乎永远不会在这些重构中幸存下来,代码总是变化很大,以至于小的单元测试被扔掉,新的测试被编写出来。由于更高级别的接口不会经常更改,因此覆盖更大功能的测试似乎在这里提供了最大的价值

对于一些琐碎的重构,比如移动方法,这些都是通过IDE完成的,因为我使用的是静态类型的语言,所以我从来没有遇到过IDE不能完美完成重构的情况


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

这是一个粒度问题,就像金发姑娘和三只熊一样。你想要的东西不要太小,不要太大,但要恰到好处

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


像任何“最佳实践”一样,这些想法通常是在理论上形成的,但需要一些常识,并根据您的具体情况进行调整,以便对您有用

我也发现了同样的问题——但我认为区分私有代码单元和公共可访问代码单元很重要。我确实认为,始终对“公共API中公开的最小、可用的代码单元”进行单元测试是很重要的

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

至于私有API,这里有一个平衡点。测试越小,您对测试的依赖性就越强。您的测试级别越高,测试就越灵活,并且越有可能通过重构


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

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

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

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


在重构类的内部算法过程中引入的一个bug会破坏90%的语义级别测试,大多数情况下,如果您经常和尽早测试,该bug会很快被发现。

听起来您的操作不是真的,这需要一个为一小部分功能编写测试的迭代周期,创建满足测试的功能,然后重构以删除测试/代码可能添加的任何重复。听起来您是在事后进行测试(“代码总是变化很大,以至于小单元测试被扔掉”)。如果一个测试是一个功能规范(就像在TDD中一样),重构将永远不会导致一个测试“无法生存”

因此,假设您不是真正在做TDD,那么您正在努力权衡要编写多少测试代码和花多少时间开发生产代码。我会说,编写足够的测试代码,这样你就知道你的代码做了它应该做的事情。如果您可以通过更粗粒度的测试来实现这一点,尽管正如其他人所说的那样,这会使您更难知道故障的原因

测试不仅仅是为了重构。很高兴知道你什么时候做完了。因此,您可以自信地添加新功能,而不会打破旧功能。因此,在你离开很久之后,其他人可以进来理解你的代码,修改它,并确信它可以工作


我建议您遵循所述的TDD实践。事后编写测试总比不编写测试好,但我发现这是一种比TDD效率低得多的实践。

我一直遵循的是BDD方法,在这种方法中,我没有测试功能,而是测试结果。进行此操作时,您仍然在测试功能,但以预期结果来衡量。我发现这样做会让你的测试更有意义、更不脆弱、更适用,结果我写的更少。

那又怎样?测试失败是一个相对罕见的事件——我看不出投入在维护微测试上的时间真的有回报。这很公平。我的意思是,肯定有一个收益递减的点。