【SpringBoot】31 核心功能 - 单元测试 - JUnit5 单元测试中的断言机制——验证你的代码是否按预期执行了 - 详解


前言

大家好,我是正在写单元测试的“小开发”一枚。最近在项目里用 JUnit5 做测试,发现了一个特别重要的东西——断言(Assertions)。一开始我也觉得“不就是判断一下结果对不对吗?”,但真正用起来才发现,它不只是“判断”,而是整个测试逻辑的“大脑”。

今天就来和大家聊聊我在实际开发中是如何一步步理解并使用 JUnit5 的断言机制 的。我会像平时写代码一样,一边讲思路,一边贴代码,顺便看看测试效果,保证你一看就懂!


一、什么是断言?为什么它这么重要?

先说点基础的。

断言(assertions),是单元测试的核心部分。它的作用是:验证你的代码是否按预期执行了

比如你写了个加法函数 add(a, b),你期望 add(2, 3) 返回 5。那你就得用断言去检查这个返回值是不是真的等于 5。

如果等于 → 测试通过 ✅
如果不等于 → 测试失败 ❌

这就是断言的作用。

在 JUnit5 中,所有断言都是静态方法,来自 org.junit.jupiter.api.Assertions 类。我们接下来一个个来看。


二、简单断言:最常用的几个

这是最基础的断言,用来做简单的值判断。

1. assertEquals

判断两个值是否相等。

@Test
@DisplayName("简单断言:相等")
public void testEquals() {
int result = 2 + 3;
assertEquals(5, result);
}

✅ 效果:如果 result 是 5,测试通过;否则报错。


2. assertNotEquals

判断两个值是否不相等。

@Test
@DisplayName("简单断言:不相等")
public void testNotEquals() {
int result = 2 + 3;
assertNotEquals(6, result);
}

✅ 如果结果不是 6,就通过;如果是 6 就失败。


3. assertSame / assertNotSame

判断两个引用是否指向同一个对象。

@Test
@DisplayName("引用相同")
public void testSame() {
String str1 = "hello";
String str2 = str1;
assertSame(str1, str2); // 会通过,因为是同一个对象
}
@Test
@DisplayName("引用不同")
public void testNotSame() {
String str1 = "hello";
String str2 = new String("hello");
assertNotSame(str1, str2); // 会通过,虽然内容一样,但对象不同
}

⚠️ 注意:assertSame 看的是内存地址,不是内容!


4. assertTrue / assertFalse

判断布尔值。

@Test
@DisplayName("布尔断言")
public void testBoolean() {
assertTrue(2 > 1);
assertFalse(2 < 1);
}

5. assertNull / assertNotNull

判断对象是否为 null。

@Test
@DisplayName("null 断言")
public void testNull() {
String str = null;
assertNull(str);
String notNull = "abc";
assertNotNull(notNull);
}

三、数组断言:对比数组是否一致

有时候我们需要比较两个数组是否相等,这时候用 assertArrayEquals

@Test
@DisplayName("数组断言")
public void array() {
int[] arr1 = {1, 2};
int[] arr2 = {1, 2};
assertArrayEquals(arr1, arr2);
}

✅ 只要顺序和内容都一样,就会通过。

如果你改成 {2, 1},就会失败。


四、组合断言:一次性验证多个条件

有时候你想一次检查多个断言,可以用 assertAll

@Test
@DisplayName("组合断言")
public void assertAll() {
assertAll(
() -> assertEquals(2, 1 + 1),
() -> assertTrue(1 > 0),
() -> assertNotNull("hello")
);
}

优点:即使其中一个失败,其他还会继续执行(不会中断),你可以看到所有问题。

比如上面如果第一个断言失败了,后面两个也会跑完,方便调试。


五、异常断言:测试代码是否抛出预期异常

以前用 JUnit4 要测异常,得用 @RuleExpectedException,很麻烦。

现在 JUnit5 有 assertThrows,超简单!

@Test
@DisplayName("异常断言")
public void testException() {
Throwable exception = assertThrows(
ArithmeticException.class,
() -> {
int result = 10 / 0;
}
);
assertTrue(exception.getMessage().contains("/ by zero"));
}

✅ 这个测试会成功,因为我们确实抛出了 ArithmeticException

如果没抛异常,或者抛了别的异常,就会失败。


六、超时断言:防止测试卡死

有些方法可能因为网络或 IO 操作太慢,导致测试一直挂住。

我们可以设置超时时间。

@Test
@DisplayName("超时断言")
public void timeoutTest() {
Assertions.assertTimeout(Duration.ofMillis(1000), () -> {
Thread.sleep(500); // 这个不会超时
});
}

✅ 如果方法执行超过 1 秒,就会抛异常。

你可以试试把 sleep(500) 改成 sleep(1500),然后看测试失败。


七、快速失败:直接让测试失败

有时候你想手动让测试失败,比如某个条件不满足时强制失败。

fail() 方法:

@Test
@DisplayName("快速失败")
public void shouldFail() {
fail("This should fail");
}

✅ 这个测试一定会失败,并输出提示信息。

这在写测试框架或模拟某些边界情况时很有用。


八、实战案例:测试 Redis 连接

我之前遇到一个问题,测试 Redis 连接的时候,连接失败了,但测试没报错,只打印了日志。

比如这样:

@Test
@DisplayName("测试 Redis")
public void testRedis() {
try {
redisTemplate.opsForValue().get("test");
} catch (Exception e) {
System.err.println("Redis 连接失败:" + e.getMessage());
}
}

但这只是打印,测试依然通过了!

这不是我们想要的。

所以我们应该用断言来捕获异常:

@Test
@DisplayName("测试 Redis 连接异常")
public void testRedisConnection() {
assertThrows(
RedisConnectionFailureException.class,
() -> redisTemplate.opsForValue().get("test")
);
}

或者更通用一点:

@Test
@DisplayName("Redis 连接应失败")
public void testRedisFail() {
Throwable exception = assertThrows(Exception.class, () -> {
redisTemplate.opsForValue().get("test");
});
assertTrue(exception.getMessage().contains("connection"));
}

这样,一旦连接失败,测试就会明确失败,而不是默默忽略。


九、总结:断言是测试的灵魂

断言类型用途
assertEquals, assertNotEquals检查值是否相等
assertSame, assertNotSame检查引用是否相同
assertTrue, assertFalse检查布尔条件
assertNull, assertNotNull检查 null
assertArrayEquals检查数组是否相等
assertAll组合多个断言
assertThrows检查是否抛出特定异常
assertTimeout设置超时时间
fail()手动让测试失败

这些断言方法就像“裁判”,帮你判断代码行为是否符合预期。


十、写在最后

刚开始写测试时,我也只是简单地用 assertEquals,后来才慢慢发现,好的断言能让测试更清晰、更健壮

比如:

  • assertThrows 而不是 try-catch
  • assertAll 避免中途失败中断
  • assertTimeout 防止测试卡住

这些都是“细节控”的体现,但正是这些细节,决定了你的测试是否可靠。

所以,别小看断言,它是你代码质量的“守护神”。


附:完整测试类示例

import org.junit.jupiter.api.*;
import java.time.Duration;
class MyTests {
@Test
@DisplayName("简单断言")
public void simpleAssert() {
assertEquals(5, 2 + 3);
assertTrue(1 > 0);
assertNotNull("hello");
}
@Test
@DisplayName("异常断言")
public void exceptionAssert() {
assertThrows(ArithmeticException.class, () -> {
int x = 10 / 0;
});
}
@Test
@DisplayName("超时断言")
public void timeoutAssert() {
Assertions.assertTimeout(Duration.ofMillis(1000), () -> {
Thread.sleep(500);
});
}
@Test
@DisplayName("组合断言")
public void allAssert() {
assertAll(
() -> assertEquals(2, 1 + 1),
() -> assertTrue(true),
() -> assertNotNull("test")
);
}
}

好了,今天的分享就到这里。希望这篇“边学边写”的博客对你有帮助。如果你也在写测试,欢迎留言交流你的断言技巧!

posted on 2025-11-30 12:26  ljbguanli  阅读(0)  评论(0)    收藏  举报