Skip to content

断言详解:让断言更易读

断言是测试的核心——测试能不能发现问题,全靠断言写得准不准。

JUnit 5 自带的断言够用,但 AssertJ 的链式断言写起来更顺滑。

JUnit 5 内置断言

基本断言

java
assertEquals(4, 2 + 2);              // 相等
assertNotEquals(5, 2 + 2);           // 不等
assertTrue(3 > 2);                    // 为真
assertFalse(3 < 2);                   // 为假
assertNull(object);                    // 为 null
assertNotNull(object);                 // 非 null
assertSame(a, b);                     // 同一引用
assertNotSame(a, b);                  // 不同引用

数组断言

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

异常断言

java
@Test
void testException() {
    assertThrows(NullPointerException.class, () -> {
        String str = null;
        str.length();
    });
}

超时断言

java
@Test
void testTimeout() {
    assertTimeout(Duration.ofSeconds(1), () -> {
        Thread.sleep(100);
    });
}

组合断言 assertAll

一次验证多个条件,全部通过才算成功:

java
@Test
void testGroupedAssertions() {
    assertAll("用户信息",
        () -> assertEquals("张三", user.getName()),
        () -> assertEquals(25, user.getAge()),
        () -> assertTrue(user.isActive()),
        () -> assertNotNull(user.getEmail())
    );
}

AssertJ:链式断言

AssertJ 是 JUnit 5 断言的首选,链式写法更易读:

对象断言

java
assertThat(user)
    .isNotNull()
    .hasName("张三")
    .hasAge(25)
    .extracting(User::getEmail)
    .isNotBlank();

集合断言

java
assertThat(users)
    .isNotEmpty()
    .hasSize(3)
    .extracting(User::getName)
    .containsExactly("张三", "李四", "王五")
    .allMatch(name -> name.length() > 0); // 全部满足条件

异常断言

java
@Test
void testExceptionWithAssertJ() {
    assertThatThrownBy(() -> {
        throw new IllegalArgumentException("参数错误: id=123");
    })
    .isInstanceOf(IllegalArgumentException.class)
    .hasMessageContaining("参数错误")
    .hasMessageContaining("123");
}

软断言 softAssertions

即使一个断言失败,后面的断言仍然会执行:

java
@Test
void testSoftAssertions() {
    SoftAssertions softly = new SoftAssertions();

    softly.assertThat(user.getName())
        .isEqualTo("张三");
    softly.assertThat(user.getAge())
        .isEqualTo(25);
    softly.assertThat(user.getEmail())
        .isNotBlank();

    softly.assertAll(); // 统一报告所有失败
}

断言选择建议

场景推荐
简单值比较JUnit 5 assertEquals
复杂对象AssertJ
多个条件组合assertAll 或 SoftAssertions
异常验证AssertJ assertThatThrownBy
集合操作AssertJ

总结

断言的写法直接影响测试的可读性和维护性:

  1. 选对工具:AssertJ 的链式断言比 JUnit 原生更易读
  2. 一个断言测一件事:别在一个 assert 里塞太多条件
  3. 失败消息要清楚:用 assertEquals(expected, actual, "失败原因") 或 AssertJ 的 as() 方法

断言是测试的眼睛,断言写得烂,问题发现不了。

基于 VitePress 构建