Skip to content

JUnit 是 Java 最主流的单元测试框架,Spring Boot 项目里 spring-boot-starter-test 自带它。这篇快速入门:怎么写测试、怎么断言、测试的生命周期。

一、解决什么问题:给代码"上保险"

单元测试 = 给单个方法/类写"验证用例"——输入一组数据,断言输出符合预期。好处:

  • 改代码不怕改坏:回归测试自动告诉你哪坏了
  • 写代码时就想清楚边界:测试逼你考虑空值、异常等场景

二、引入依赖

Spring Boot 项目已经自带(spring-boot-starter-test)。纯 Java 项目加:

xml
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.10.0</version>
    <scope>test</scope>
</dependency>

三、第一个测试

java
// 被测类
public class Calculator {
    public int add(int a, int b) { return a + b; }
    public int divide(int a, int b) { return a / b; }
}
java
import org.junit.jupiter.api.*;

class CalculatorTest {

    private Calculator calc;

    @BeforeEach                       // 每个测试方法前执行
    void setUp() {
        calc = new Calculator();
    }

    @Test
    void add_两个数相加() {
        assertEquals(5, calc.add(2, 3));   // 断言:期望 5,实际结果
    }

    @Test
    void divide_除零抛异常() {
        assertThrows(ArithmeticException.class, () -> calc.divide(1, 0));
    }
}

运行方式:IDEA 里点方法/类左边的绿色三角,或 mvn test

四、核心断言

断言作用
assertEquals(期望, 实际)相等(基本类型/对象)
assertTrue / assertFalse布尔判断
assertNull / assertNotNull空判断
assertThrows(异常类, 表达式)断言抛出指定异常
assertAll(...)多个断言一起执行,全过才过

五、测试生命周期

测试有 4 个"钩子"注解,分两对:@All(类级,只执行一次)@Each(方法级,每个测试都执行)

注解执行时机典型用途
@BeforeAll / @AfterAll整个类所有测试前/后,只执行一次(必须静态方法)初始化/关闭连接池、数据库连接
@BeforeEach / @AfterEach每个测试方法前/后各执行一次准备/清理单条测试数据

执行顺序(一个类有 3 个测试时):

JUnit 生命周期执行顺序

用一个能直接跑的类看真实输出(3 个测试方法 + 计数器):

java
class OrderServiceTest {

    static int initCount = 0;   // 统计 @BeforeAll/@AfterAll 执行次数

    @BeforeAll
    static void initAll() {     // 只执行 1 次:整个类开始前
        initCount++;
        System.out.println("[BeforeAll] 第 " + initCount + " 次:初始化连接池");
    }

    @AfterAll
    static void tearDownAll() { // 只执行 1 次:整个类结束后
        initCount++;
        System.out.println("[AfterAll]  第 " + initCount + " 次:关闭连接池");
    }

    @BeforeEach
    void init() {               // 每个测试前都执行
        System.out.println("  [BeforeEach] 准备数据");
    }

    @AfterEach
    void cleanup() {            // 每个测试后都执行
        System.out.println("  [AfterEach]  清理数据");
    }

    @Test
    void test1() { System.out.println("  [测试1] 执行"); }
    @Test
    void test2() { System.out.println("  [测试2] 执行"); }
    @Test
    void test3() { System.out.println("  [测试3] 执行"); }
}

运行输出(javac + JUnit ConsoleLauncher 实跑验证,4 tests successful):

[BeforeAll] 第 1 次:初始化连接池        ← ① 整个类开始前,只 1 次
  [BeforeEach] 准备数据
  [测试1] 执行
  [AfterEach]  清理数据
  [BeforeEach] 准备数据                  ← ② 每个测试前都来一遍
  [测试2] 执行
  [AfterEach]  清理数据
  [BeforeEach] 准备数据
  [测试3] 执行
  [AfterEach]  清理数据
[AfterAll]  第 2 次:关闭连接池          ← ③ 整个类结束后,只 1 次

核心区别一句话@All 管"整个类只做一次的事"(重资源,建一次省反复建);@Each 管"每个测试都要的事"(数据准备/清理,保证测试相互独立——A 测试留下的数据不会污染 B 测试)。

小结

  • JUnit 5 是 Java 单元测试标准,Spring Boot 自带
  • 核心:@Test + 断言方法(assertEquals/assertThrows 等)
  • 生命周期:@BeforeAll(一次)/ @BeforeEach(每个前)
  • 一个方法一个测试,测正常 + 测异常边界

想了解怎么用 Mockito 模拟依赖(测 Service 不依赖真实 DB),看「Mockito 测试替身」(文章整理中)。