面对现实吧!没有人真的喜欢做单元测试。有很多人向我讲述他们超级讨厌单元测试。尽管有些人擅长于此,但对于我们大多数人而言,不管有多少抱怨、多少反感,单元测试都是一件必不可少的事情。今天,我将探讨为什么我们不喜欢单元测试的一些原因,以及如何通过软件自动化克服这些障碍。
那么为什么要进行单元测试呢?
大多数开发团队都会同意,尽管他们不喜欢它,但是单元测试实际上是有价值的。它可以帮助开发人员真正理解他们正在开发的代码,并为连续测试金字塔打下坚实的基础(如下图所示),从而使团队能够加快敏捷开发的速度,同时减少缺陷滑入开发管道后期的风险。
我会更进一步地说,创建单元测试的过程本身就是一项有益的活动,它可以帮助开发人员通过不同的角度查看他们的代码,实质上是进行额外的代码审查。
在进行单元测试时,你可以从外部角度查看该功能的界面,并从诸如“如何使用我的代码?”之类的问题中受益。(从而简化了界面并降低了代码维护成本),或者,如果我收到无效的数据该怎么办?(引导我们写出更健壮和可重用的代码)。
如此重要的单元测试为啥就不受人待见?
通常,开发团队进行单元测试的数量很少或完全不进行,一般是由于以下因素综合造成的:(1)提供越来越多的功能的压力(和花费的时间),以及(2)复杂性和时间——创建有价值的单元测试的消耗性质。
这可以归结为开发人员列举的将限制采用单元测试作为核心开发实践的一些常见原因,包括:
- 难以理解、初始化和/或隔离被测单元的依赖性。
- 确定要验证的内容并定义适当的断言非常耗时,并且通常需要聪明的“猜测工作”。
- 涉及很多手动编码,通常甚至比实现特定功能或增强功能所需的还要多。
- 只是没有那么有趣……开发人员不想感觉像是测试人员,他们想花时间交付更多功能。
为了解决这些限制,目前有几种现有的工具可以帮助进行单元测试。单元测试和断言框架提供了标准化的执行格式(即Junit),以无缝集成到CI基础架构中(例如Jenkins,Bamboo,TeamCity)。IDE有助于创建测试代码(例如Eclipse,IntelliJ)。模拟框架将代码与其依赖项隔离开(例如Mockito,PowerMock)。代码覆盖工具可让你对执行的代码有一些了解(例如Emma,Cobertura,Clover)。调试器允许开发人员监视和检查单个测试的分步执行。
但是不幸的是,所有这些工具都有局限性,并且开发人员仍然发现了许多难点,例如:
- IDE帮助创建单元测试的框架,但没有“内容”。开发人员仍然需要添加大量代码来创建执行测试:
- 需要手动定义断言,并且必须执行测试以查看是否已断言正确的值。
- 模拟框架需要大量的手动代码来实例化和配置,以及有关如何正确使用它们的知识。
- 覆盖率工具可以洞悉已执行的测试涵盖了哪些代码,但不能洞悉测试的运行时行为。
- 调试器可用于单个测试,但不能扩展为监视整个测试运行的方式。
总之,在开始向测试中添加业务逻辑之前,单元测试的创建仍然需要大量的手动、费时且经常令人费解的工作。
何以解忧?我们“雇佣”了一个助手!
我们发现Parasoft Jtest的单元测试助手可以减轻单元测试的“痛苦”,毕竟,虽说我们讨厌单元测试,但是我们也知道它能给我们带来的价值远远超过了它带来的麻烦。所以长痛不如短痛!
为了帮助自己绕过这些痛点,我们开始评估软件测试自动化工具。现在,可以使用Parasoft Jtest的单元测试助手来解决这些问题,只需单击按钮来创建功能齐全的单元测试。
使用UTA创建的测试只是“常规” JUnit,但是它为你完成了所有繁琐的工作。UTA设置测试框架,实例化对象,并为被测试方法所使用的适当对象和方法调用配置模拟。
这些JUnit可以作为标准CI工作流的一部分执行,就像执行现有测试一样。但是,当UTA执行JUnit(包括你现有的测试)时,对测试的监视方式不仅可以提供代码覆盖范围,还可以提供分析功能。
通过在运行时分析测试,UTA可以提供一系列建议,其中许多具有快速修复的功能,可帮助你一键式执行操作,例如:
- 突出显示已更改并应声明的对象值,
- 确定应该模拟的方法调用,以更好地隔离被测代码,以及
- 查找无法自动“清理”的测试并创建潜在的不稳定测试环境(例如,由于使用了线程、外部文件、静态字段或系统属性)。
我们使用UTA来缓解单元测试的痛苦,因为作为一个10多年专门从事软件开发和项目定制解决方案的码农团队,我们知道单元测试是创建安全、保障、可靠和高质量的软件的重要步骤。
如果你也有和我一样的顾虑与烦恼,欢迎留言讨论。当然,如果你尝试过更好的单元测试方法,也希望不吝赐教,分享给我哟。