我公司的人认为单元测试是很多额外的工作,与现有的功能测试相比,它提供的好处更少。单元和集成测试值得吗?请注意在设计时并未考虑测试的大型现有代码库。
9 回答
大多数人不知道自动化单元测试的用途:
- 尝试新技术
- 记录如何使用部分代码
- 确保死虫保持死状态
- 为了让你重构代码
- 允许您更改代码的任何主要部分
- 创建一个较低的水印,低于该水印,您的产品质量不可能下降
- 提高开发速度,因为现在,您知道某些东西有效(而不是希望它在客户报告错误之前有效)。
因此,如果这些原因中的任何一个给您带来好处,那么自动化单元测试就适合您。如果没有,那么不要浪费你的时间。
(我假设您使用“功能测试”来表示涉及整个系统或应用程序启动并运行的测试。)
我会在编写新功能时对新功能进行单元测试,原因有以下三个:
- 它可以帮助我更快地编写工作代码。“单元测试失败,修复代码,单元测试通过”的周转时间通常比“功能测试失败,修复代码,功能测试通过”要短很多。
- 它帮助我以更简洁的方式设计我的代码
- 它可以帮助我理解我的代码以及在我来维护它时它意味着什么。如果我做出改变,它会让我更有信心,我没有破坏任何东西。
(这包括 Epaga 建议的错误修复。)
我强烈推荐 Michael Feathers 的“Working Effectively with Legacy Code”,为您提供有关如何开始对不是为它设计的代码库进行单元测试的提示。
这取决于您的功能测试是自动完成还是手动完成。如果是后者,那么任何类型的自动化测试套件都是有用的,因为运行这些单元/集成测试的成本远低于运行手动功能测试。您可以在那里显示真实的投资回报率。我建议从编写一些集成测试开始,如果将来时间/预算允许,那么看看单元测试。
为遗留代码追溯编写单元测试通常是不值得的。坚持功能测试,并自动化它们。
然后我们所做的是制定任何错误修复(或新功能)必须伴随单元测试的指南,至少测试修复。这样,您至少可以使项目朝着正确的方向发展。
而且我必须同意 Jon Skeet(我怎么能不同意?)推荐“有效地使用旧代码”,这确实是一个有用的略读/阅读。
碰巧,我昨晚读了一篇关于这个主题的论文。作者比较了 Microsoft 和 IBM 四个小组内的项目,事后对比了同时使用单元测试和功能测试的项目以及单独使用功能测试的项目。引用作者的话:
“案例研究的结果表明,与未使用 TDD 实践的类似项目相比,这四种产品的预览版缺陷密度降低了 40% 到 90%。主观上,团队的初始版本增加了 15% 到 35%。采用 TDD 后的开发时间。”
这表明当您向项目添加新功能时,进行单元测试当然是值得的。
是的,它们是值得的,自从我开始对我的代码进行单元测试以来,我现在的编码速度更快了。我花更少的时间修复错误,而花更多的时间思考我的代码应该做什么。
我被买来咨询 FAT(测试)的一个应用程序包含一个 21,000 行的 switch 语句。大多数功能单元在案例语句中只有几十到几百行。该应用程序是在几个变体中构建的,因此开关中有许多#ifdef 部分。
它不是为单元测试而设计的——它根本没有被考虑在内。
(它是在某种意义上设计的,有一个明确的、易于理解的体系结构 - malloc 一个结构,向主循环发送一条用户消息,其中包含指向该结构的指针作为 lparam,然后在处理消息时释放它。但形式确实不遵循功能,这是好的设计的中心原则。)
将单元测试添加到新功能将意味着与模式的重大突破;要么您需要将代码放在大开关以外的地方,并使变体选择机制的复杂性加倍,要么制作大量脚手架将消息放入队列中以触发新功能。
因此,尽管对新功能进行单元测试肯定是可取的,但如果系统还没有很好地考虑因素,这并不总是可行的。要么有大量工作来重构系统以允许单元测试,要么您最终对代码进行基准测试并将其剪切并粘贴到现有框架中 - 单元测试代码的副本不是单元测试代码。
当您想了解某事时,您进行测试。如果您知道您的产品(系统、单元、服务、组件...)可以正常工作,则无需对其进行测试。如果您不确定它是否会起作用,您可能对此有一些疑问。这些问题是否值得回答是风险和优先事项的问题。
如果你确定你的产品可以工作,并且你对它没有任何疑问,那么还有一个问题值得问:为什么我没有任何问题?
---迈克尔 B.
单元测试确实是额外的工作,但从长远来看它是有回报的。以下是集成测试的优势:
- 你会得到一个回归套件,它在重构时充当安全网——集成测试也是如此,尽管很难说测试是否涵盖了一段代码。
- 单元测试在修改代码时会立即给出反馈——这种反馈可能非常准确,指向异常所在的方法。
- 这些测试运行起来很便宜:它们运行速度非常快(通常几秒钟),无需任何安装或部署,只需编译和测试。所以它们可以经常运行。
- 一旦发现问题,很容易添加一个新测试来重现问题,它会增加回归套件或回答问题(“如果不使用空参数调用此函数会发生什么......”)。
两者之间显然有一些重叠,但它们是互补的,因为它们都具有优势。
现在,就像任何软件工程过程一样,测试必须根据项目需要进行定制。
对于大型遗留代码库,即未进行单元测试的遗留代码,我建议将单元测试限制为添加到代码中的新功能,因为单元测试可能很难引入。在这方面,我只能第二(第三?)“有效地使用遗留代码”一书的建议,以帮助将单元测试引入现有代码库。