Part 01 什么是单元测试
单元测试是一种软件测试方法,用于测试软件系统的最小可测试单元,例如函数、方法或类的行为。单元测试通常由开发人员编写,并在编写代码时就开始执行。这样可以保证实时检测代码中的错误、缺陷和潜在的问题,确保代码满足预期的行为和输出。
单元测试可以分为如下几个步骤,然后在开发中就可以不断地编写、执行、分析测试用例,并修复问题。
- 确定测试目标:在编写单元测试之前,开发人员需要明确测试目标和预期结果。这有助于确保测试的准确性和完整性。
- 编写测试用例:测试用例是单元测试的核心。测试用例应该覆盖代码的各种情况和条件,并检测其行为和输出。
- 执行测试用例:测试用例可以手动执行,也可以通过自动化测试框架执行。自动化测试框架可以帮助开发人员更快速和有效地执行测试用例,并自动报告测试结果。
- 分析测试结果:分析测试结果可以帮助开发人员更好地理解代码的行为和输出,发现问题和改进代码。
- 修复问题:在发现问题之后,开发人员需要及时修复问题,确保代码的质量和稳定性。修复问题后,需要重新运行测试用例,确保问题已经解决并且没有引入新的问题。
Part 02 单元测试的作用
- 确保代码质量:单元测试可以帮助开发人员检测代码中的错误、缺陷和潜在的问题。通过及时发现和修复这些问题,可以保证代码的质量和稳定性。
- 提高代码可维护性:单元测试可以帮助开发人员更好地理解代码,了解其行为和预期输出,这使得代码更易于维护和修改。
- 提高开发效率:通过早期发现和解决问题,可以减少后期的调试时间和资源成本,提高开发效率。
- 促进团队合作:单元测试可以作为开发团队的交流和协作工具。团队成员可以分享代码和测试结果,并共同解决问题。
- 改进设计和架构:单元测试可以促进更好的设计和架构实践。通过编写可测试的代码和测试用例,可以帮助开发人员更好地理解系统的组成部分,并促进设计和架构的优化。
Part 03 Java项目中单元测试方案推荐
- Junit5
JUnit是Java领域内最为流行的单元测试框架,Junit测试又称白盒测试,旨在验证被测试的软件如何(How)完成功能和完成什么样(What)的功能。Junit的最新版本Junit 5集合了 Junit Platform、Junit Jupiter、Junit Vintage等。其中,Junit Platform是在JVM上启动测试框架的基础;Junit Jupiter提供了新的编程模型,包含了一个测试引擎,在Junit Platform上运行;Junit Vintage 提供了兼容JUnit4.x,Junit3.x的测试引擎,帮助老项目依赖包的过度升级。Springboot2.2.0+中默认集成:
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.23</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.23</version>
</dependency>
常用注解及说明如下:
@Test :表示方法是测试方法(即框架操作对象),与JUnit4的@Test不同,JUnit5的@Test非常单一不能声明任何属性,拓展的测试都由Jupiter提供
@DisplayName :为测试类或者测试方法设置展示的名称
@BeforeAll :表示在所有单元测试方法之前执行
@AfterAll :表示在所有单元测试方法之后执行
@BeforeEach :表示在每个单元测试方法之前执行
@AfterEach :表示在每个单元测试方法之后执行
@Timeout :表示测试方法运行超过指定时间将会抛出TimeoutException异常
@Disabled :表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
@RepeatedTest :表示方法需要重复执行的次数
@ExtendWith :为测试类或测试方法提供@Autowired的IOC注入
- JMH
JMH(Java Microbenchmark Harness)是java领域用于代码微基准测试的工具套件,主要是基于方法层面的基准测试,精度可以达到纳秒级,它是由Java虚拟机团队开发的。当你定位到热点方法,希望进一步优化方法性能的时候,就可以使用 JMH 对优化的结果进行量化的分析
springboot集成方式导入依赖包如下(最新版本1.36):
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.23</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.23</version>
</dependency>
JMH的应用场景如下:
(1)想准确地知道某个方法需要执行多长时间及执行时间和输入之间的相关性;
(2)对比接口不同实现在给定条件下的吞吐量;
(3)查看多少百分比的请求在多长时间内完成。
上述两个方案前者是确保代码运行的正确性,后者旨在测试代码运行的性能,在项目实战中可以做到强强联合,确保代码质量和稳定性,帮助完善或改进设计和架构,编写高质量的单元测试需要遵循最佳实践。
Part 04 总结
单元测试的目的是为了验证软件开发的功能、性能、完整性。当软件发生变化时,单元测试可以帮助开发人员确定哪些部分受到影响,以及如何更改代码。还可以帮助开发人员了解他们的代码,从单元测试中获得反馈,从而更好继续开发软件。文中提到的基于JUnit5和JMH两种互补方案,可以从代码功能和性能两个角度保证软件交付成果。
💡 参考文献
[1] 蔡高亮,2008, 软件单元测试[J],http://www.its.cesi.cn/qkContent/articleDetail/1043,2023/3/13.