1. 单元测试入门:什么是单元测试、为什么需要单元测试、JUnit 5 环境搭建与第一个测试用例
1.1 什么是单元测试?
单元测试,说白了就是测试代码里最小的可测试单元。在 Java 里,这个单元通常是一个方法。你写了一个方法,然后写另一段代码去验证它——这就是单元测试。
我刚开始带团队时,发现很多同事把单元测试和集成测试混为一谈。其实区别很简单:单元测试不依赖外部资源,比如数据库、文件系统、网络服务。它只测你写的那个方法本身。如果测试里连了数据库,那就不叫单元测试了,叫集成测试。
举个例子,你写了个计算器类:
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
单元测试就是验证 add(2, 3) 是不是返回 5。就这么简单。
1.2 为什么需要单元测试?
这个问题我经常被问到。说实话,十年前我也觉得单元测试浪费时间。直到有一次,我改了一个工具类里的日期格式化方法,自以为没问题,结果上线后订单系统全崩了——因为那个方法被十几个模块调用,我改的边界条件影响了核心逻辑。
从那以后,我再也不敢不写单元测试了。你想想看,没有单元测试,你改代码就像在雷区里走路,每一步都提心吊胆。
单元测试的核心价值有这几点:
- 快速反馈:写完代码就能跑,几秒钟知道对错
- 安全重构:改代码时,测试用例就是你的安全网
- 活的文档:测试代码比注释靠谱,它展示了方法到底怎么用
- 设计驱动:如果一个方法很难写测试,说明设计有问题
核心观点:单元测试不是测试人员的责任,是开发者的基本功。我面试高级工程师时,必问单元测试相关的问题。
1.3 JUnit 5 环境搭建
JUnit 5 是目前 Java 生态里最主流的测试框架。它由三个组件组成:
| 组件 | 作用 |
|---|---|
| JUnit Platform | 启动测试框架的基础平台 |
| JUnit Jupiter | 新的编程模型和扩展机制 |
| JUnit Vintage | 兼容 JUnit 4 和 3 的老版本测试 |
我个人习惯用 Maven 管理项目。在 pom.xml 里加上依赖:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
如果你用 Gradle,在 build.gradle 里加:
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
小技巧:我建议用最新稳定版。去 mvnrepository.com 搜 "junit-jupiter" 就能看到最新版本号。
1.4 第一个测试用例
环境搭好了,我们来写第一个测试。假设我们有这样一个工具类:
public class StringUtils {
public static boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
}
测试类长这样:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class StringUtilsTest {
@Test
void testIsEmptyWithNull() {
assertTrue(StringUtils.isEmpty(null));
}
@Test
void testIsEmptyWithEmptyString() {
assertTrue(StringUtils.isEmpty(""));
}
@Test
void testIsEmptyWithWhitespace() {
assertTrue(StringUtils.isEmpty(" "));
}
@Test
void testIsEmptyWithNonEmpty() {
assertFalse(StringUtils.isEmpty("hello"));
}
}
这里有几个关键点:
@Test注解告诉 JUnit 这是一个测试方法assertTrue和assertFalse是断言方法,用来验证结果- 测试方法名要清晰,让人一看就知道在测什么
注意:测试类名通常用 被测试类名 + Test 的命名方式。比如 StringUtils 的测试类就叫 StringUtilsTest。这是行业惯例,我建议你遵守。
1.5 运行测试
在 IDE 里,直接右键点击测试类或方法,选择 "Run" 就行。IntelliJ IDEA 和 Eclipse 都原生支持 JUnit 5。
如果用 Maven 命令行:
mvn test
你会看到类似这样的输出:
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
看到 "Tests run: 4, Failures: 0" 就说明全部通过了。嗯,这种感觉很爽。
1.6 避坑指南
我曾经犯过一个低级错误:测试方法写成 private。JUnit 5 要求测试方法必须是 public 或者包级可见(不写修饰符)。写成 private 的话,测试会被静默跳过,你都不知道。
还有一点,测试方法不能有返回值,必须是 void。这也是 JUnit 的硬性要求。
我的习惯:每个测试方法只测一个场景。一个方法里塞十几个断言,出了问题你都不知道是哪个断言挂了。一个方法一个断言,清晰明了。
1.7 小结
单元测试不是什么高深的技术,它就是写代码验证代码。但它的价值,只有经历过线上事故的人才能真正体会。从今天开始,每写一个方法,顺手写个测试。坚持一个月,你会感谢自己的。
下一章,我们会深入 JUnit 5 的核心注解和断言,到时候见。