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) |
| 链式调用 | ❌ | ✅ |
| 失败消息 | 基础 | 详细友好 |
| 集合断言 | 基本 | 丰富强大 |
| 软断言 | assertAll | SoftAssertions |
| 文件断言 | ❌ | ✅ |
| 学习曲线 | 低 | 中 |
| 依赖 | 内置 | 需额外引入 |
相关页面
- **Java\JUnit5-完全指南** - 返回总览
- **Java\JUnit5-注解体系** - 生命周期与标记注解
- **Java\Mockito-完全指南** - Mock 配合断言使用
- **Java\SpringBoot-测试专题** - Spring 环境下的断言
#标签 #junit #断言 #assertion #assertj #testing