Skip to content

JUnit 5 断言完全指南

验证预期结果与实际结果是否一致


断言概述

断言(Assertion)是单元测试的核心,用于验证预期结果与实际结果是否一致。JUnit 5 的断言位于 org.junit.jupiter.api.Assertions 包中。


基础断言

assertEquals / assertNotEquals

java
// 基本类型
assertEquals(2, 1 + 1);
assertEquals("hello", "hello");

// 浮点数比较(需要 delta)
assertEquals(0.1 + 0.2, 0.3, 0.0001); // delta 容许误差

// 自定义消息
assertEquals(2, result, "计算结果应为 2");

assertTrue / assertFalse

java
assertTrue(list.isEmpty());
assertFalse(user.isActive());

// 带消息
assertTrue(isValid, "输入应通过验证");

assertNull / assertNotNull

java
assertNull(optionalResult);
assertNotNull(user.getId());

assertSame / assertNotSame

java
// 验证是同一个对象(引用相等)
User original = new User();
User copy = original;
assertSame(original, copy);  // ✅ 同一个引用

User another = new User();
assertNotSame(original, another);  // ✅ 不同对象

异常断言

assertThrows

java
@Test
void divisionByZeroShouldThrow() {
    ArithmeticException ex = assertThrows(
        ArithmeticException.class,
        () -> calculator.divide(10, 0)
    );
    
    assertEquals("Division by zero", ex.getMessage());
}

assertDoesNotThrow

java
@Test
void validDivisionShouldNotThrow() {
    assertDoesNotThrow(() -> calculator.divide(10, 2));
}

assertThrowsExactly(JUnit 5.9+)

java
@Test
void shouldThrowExactlyNullPointerException() {
    // 仅匹配 NullPointerException,不接受其子类
    assertThrowsExactly(NullPointerException.class, 
        () -> Objects.requireNonNull(null)
    );
}

超时断言

assertTimeout

java
@Test
void shouldCompleteWithinTimeLimit() {
    String result = assertTimeout(
        Duration.ofMillis(100),
        () -> {
            // 模拟耗时操作
            Thread.sleep(50);
            return "done";
        }
    );
    assertEquals("done", result);
}

assertTimeoutPreemptively

java
@Test
void shouldTimeoutPreemptively() {
    // ⚠️ 立即中断,不等待操作完成
    assertTimeoutPreemptively(
        Duration.ofMillis(50),
        () -> {
            Thread.sleep(1000); // 会被立即中断
            return "done";
        }
    );
}

两者的区别:

模式行为适用场景
assertTimeout等待操作完成后验证允许操作完成
assertTimeoutPreemptively立即中断执行需要强制停止耗时操作

组合断言 assertAll

关键特性:所有子断言都会执行并报告,而不是遇错即停。

java
@Test
@DisplayName("用户信息验证")
void validateUser() {
    assertAll("用户信息完整性",
        () -> assertNotNull(user.getId(), "ID 不能为空"),
        () -> assertEquals("张三", user.getName(), "姓名应为张三"),
        () -> assertTrue(user.getAge() > 0, "年龄必须大于 0"),
        () -> assertTrue(user.getAge() < 150, "年龄必须在合理范围内"),
        () -> assertMatches(user.getEmail(), ".+@.+\\..+", "邮箱格式不正确"),
        () -> assertFalse(user.isDeleted(), "用户不应被标记删除")
    );
}

嵌套 assertAll

java
@Test
void complexValidation() {
    assertAll("订单验证",
        () -> assertAll("基本信息",
            () -> assertNotNull(order.getId()),
            () -> assertEquals("PENDING", order.getStatus())
        ),
        () -> assertAll("金额验证",
            () -> assertTrue(order.getTotal() > 0),
            () -> assertEquals(order.getSubtotal() + order.getTax(), 
                             order.getTotal(), 0.01)
        ),
        () -> assertAll("关联数据",
            () -> assertFalse(order.getItems().isEmpty()),
            () -> assertNotNull(order.getCustomer())
        )
    );
}

数组与集合断言

assertArrayEquals

java
@Test
void arrayEquality() {
    int[] expected = {1, 2, 3};
    int[] actual = {1, 2, 3};
    assertArrayEquals(expected, actual);
}

assertIterableEquals

java
@Test
void iterableEquality() {
    List<String> expected = List.of("A", "B", "C");
    List<String> actual = List.of("A", "B", "C");
    assertIterableEquals(expected, actual);
}

第三方断言库:AssertJ

AssertJ 提供了流式 API,比原生 JUnit 断言更易读、更强大。

Maven 依赖

xml
<dependency>
    <groupId>org.assertj</groupId>
    <artifactId>assertj-core</artifactId>
    <version>3.25.3</version>
    <scope>test</scope>
</dependency>

核心语法

java
import static org.assertj.core.api.Assertions.assertThat;

// 基本类型
assertThat(2 + 2).isEqualTo(4);
assertThat("hello").isNotEmpty().contains("ell");

// 数值比较
assertThat(3.14)
    .isGreaterThan(3.0)
    .isLessThan(4.0)
    .isCloseTo(3.1, within(0.1));

字符串断言

java
assertThat("Hello World")
    .startsWith("Hello")
    .endsWith("World")
    .contains("lo")
    .doesNotContain("xyz")
    .matches("\\w+ \\w+");

集合/数组断言

java
// List 断言
assertThat(list)
    .hasSize(3)
    .contains("A", "B", "C")
    .containsExactly("A", "B", "C")    // 顺序必须完全一致
    .containsExactlyInAnyOrder("C", "B", "A")  // 无视顺序
    .doesNotContain("X");

// Map 断言
assertThat(map)
    .hasSize(2)
    .containsEntry("key1", "value1");

// 过滤后的断言
assertThat(users)
    .filteredOn(u -> u.getAge() > 18)
    .hasSize(2)
    .extracting(User::getName)
    .containsExactly("张三", "李四");

SoftAssertions(软断言)

java
void manualSoftAssertions() {
    try (SoftAssertions softly = new SoftAssertions()) {
        softly.assertThat(result).isEqualTo(expected);
        softly.assertThat(list).isNotEmpty();
        softly.assertAll();  // 统一报告所有错误
    }
}

断言对比总结

特性JUnit 原生AssertJ
语法风格assertEquals(a, b)assertThat(a).isEqualTo(b)
链式调用
失败消息基础详细友好
集合断言基本丰富强大
软断言assertAllSoftAssertions
文件断言
学习曲线
依赖内置需额外引入

相关页面

  • **Java\JUnit5-完全指南** - 返回总览
  • **Java\JUnit5-注解体系** - 生命周期与标记注解
  • **Java\Mockito-完全指南** - Mock 配合断言使用
  • **Java\SpringBoot-测试专题** - Spring 环境下的断言

#标签 #junit #断言 #assertion #assertj #testing

Knowledge4J — Java 知识库