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 这是一个测试方法
  • assertTrueassertFalse 是断言方法,用来验证结果
  • 测试方法名要清晰,让人一看就知道在测什么

注意:测试类名通常用 被测试类名 + 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 的核心注解和断言,到时候见。