单元测试与测试驱动开发

在学着做lept_json这个项目时,作者在教程中介绍了单元测试的概念,并使用测试驱动开发(Test Driven Development)的方法来循序渐进地完善程序的功能。这篇博客就简单地展开讲一下单元测试和测试驱动开发,并介绍一下在Java中如何使用JUnit库来对代码进行单元测试。

单元测试

单元测试其实挺好理解的,平时在写代码时,有经验的程序员经常会将不同的功能拆分成函数,这样便于对每个函数的正确性进行验证。单元测试大概就是利用这样的思想,对每个模块的功能进行分开检查,以便更快地找到错误所再。其实我们平时写一些简单的程序也会用到单元测试,只不过我们往往会在整个程序运行出错后再进行开发,并且往往是通过调用coutprintf等输出函数来检查的。

“正规”的单元测试,是使用断言或第三方的库来对程序的各个功能进行验证的,稍后会介绍用于单元测试Java的JUnit库。

测试驱动开发

前面提到过,单元测试是对程序的子模块进行正确性验证,测试驱动开发就是将验证这一步提前到开发前,也就是先编写测试代码确定需要的功能,再进行开发,使得代码能通过测试。

测试驱动开发流程示意图

测试驱动开发的好处有两点,一是可以在开发前就有明确的目标,在开发过程中可以专注功能的实现;二是在功能代码编写完成后,可能需要对代码进行优化、重构,在此过程中有可能会造成功能代码出错,使用测试驱动开发就可以在保持单元测试通过的前提下,随意地进行代码的重构。

当然,测试驱动开发也有缺点,测试开发可能会拖慢开发的进度。另外,一些场景也不适合使用测试驱动开发,例如数据驱动的算法(深度学习)、有物理边界的开发(硬件开发)、无法快速测试的开发等。

JUnit库

这里仅介绍JUnit5。

JUnit是Java语言的比较著名的单元测试库,可以在Maven中加入以下依赖来引入:

1
2
3
4
5
6
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.1</version>
<scope>test</scope>
</dependency>

开发习惯

在进行Java开发(其他语言应该也类似),会在放置源码的同级目录下放一个test包,其中放着专门用于测试的代码。如果使用Maven进行项目构建,Maven也会帮你自动创建相关的包。

实际上,不用这么按部就班也可以将代码跑起来,但是这样可以让代码看起来更规范,就像main函数执行成功通常返回0一样。

程序入口

使用JUnit库进行测试时,不需要将main函数作为程序入口,只需要在你希望进行的单元测试函数前面加上@Test注解即可,便可以在IDEA中直接运行这个单元测试。

断言

断言类似C中的assert,当条件不满足时就会提前终止测试并提供相关信息。只不过JUnit5提供了多种封装,方便在多种数据之间进行比较,常用到的有:assertEqualsassertNotEqualsassertTrueassertThrows等,用法也很简单,望文生义就可以知道它们的用途。

但是令人非常难以接受的一点是,这些函数都是Assertions类的静态成员函数,使用时必须通过Assertions类调用,IDEA似乎并不会自动从类中导入这些静态成员函数,所以如果图省事需要手动import

1
import static org.junit.jupiter.api.Assertions.*;

注解

注解类 作用
@RepeatedTest(n) 重复测试n次
@DisplayName(name) 设置显示名称,``name`可以包含特殊字符
@BeforeEach/@AfterEach 在每个测试前/后运行的函数(不能为静态函数)
@BeforeAll/@AfterAll 所有测试开始前/后运行的函数(必须为静态函数)

单元测试与测试驱动开发
http://zhouhf.top/2024/10/19/Unit-Test/
作者
周洪锋
发布于
2024年10月19日
许可协议