断言机制详解
JUnit 的断言,说白了就是帮你「自动拍板」——测试跑完,结果对不对?断言说了算。我之前带新人时总强调:测试代码里 90% 的逻辑都在断言里。写不好断言,测试就跑偏了。
这一节咱们把最常见的断言方法掰开揉碎,看看它们各自什么时候用、怎么用不出错。先上一张图,把整个断言家族捋一遍:
assertEquals —— 最常用的值比较
assertEquals(expected, actual) 应该是我写过的测试里出现频率最高的断言。它检查两个值是否「逻辑相等」——注意,不是引用相等,而是通过 equals() 方法比较。
常用场景:
- 校验方法返回值是否匹配预期
- 检查对象属性是否被正确设置
- 比较字符串、数字、日期等值类型
@Test
void testCalculateTotal() {
double result = order.calculateTotal(100.0, 0.2);
assertEquals(120.0, result, 1e-6); // 第三个参数是delta(浮点数精度)
}
正确写法:
assertEquals(120.0, result, 0.001)
assertTrue / assertFalse —— 布尔条件验证
当你只需要判断一个条件是 true 还是 false 时,用这俩。简单直接。
@Test
void testUserAccess() {
User user = new User("admin", "ADMIN");
assertTrue(user.hasRole("ADMIN"), "管理员应拥有ADMIN角色");
assertFalse(user.isLocked(), "新用户不应被锁定");
}
我个人习惯:能写出清晰的条件表达式时,优先用 assertTrue。如果条件很复杂,先提取成布尔变量再断言,可读性会好很多。
assertNull / assertNotNull —— 空值检查
这个方法其实挺容易被忽视的。很多人喜欢用 assertEquals(null, obj),但 JUnit 专门提供了语义化的方法,为什么不用?
避坑指南:我曾经在重构时把一个 return 语句漏掉了,返回值是 null。因为用 assertEquals 比较,测试居然没发现——因为预期值也是 null。换成 assertNotNull 后,立马揪出 bug。
@Test
void testFindUserById() {
User user = userService.findById(1L);
assertNotNull(user, "用户ID=1 应该存在");
User missing = userService.findById(-1L);
assertNull(missing, "不存在的用户应返回 null");
}
assertSame / assertNotSame —— 引用相等性
注意!assertSame 比较的是 内存地址(即 ==),不是 equals()。什么时候用?
- 验证单例模式:同一个实例
- 检查缓存是否命中:返回同一个对象引用
- 确保没有创建不必要的副本
@Test
void testSingleton() {
Cache instance1 = Cache.getInstance();
Cache instance2 = Cache.getInstance();
assertSame(instance1, instance2, "单例应返回同一实例");
}
assertArrayEquals —— 数组内容比较
数组用 assertEquals 比较的是引用,永远不相等。必须用 assertArrayEquals,它会逐一比较每个元素。
@Test
void testGetUserIds() {
List<Long> ids = userService.getAllUserIds();
assertArrayEquals(new Long[]{1L, 2L, 3L}, ids.toArray(), "用户ID列表不匹配");
}
多重断言与灵活断言
JUnit 5 还提供了 assertAll 和 assertThrows 等高级方法。不过本章咱们先聚焦核心,这两个留到后面章节再细讲。
重要提示
每个断言方法都支持一个可选的 failure message(失败消息)作为第一个参数。我强烈建议你加上,哪怕简单写一句“期望xx不为空”。否则测试失败时只看得到 expected: null but was: xxx,排查起来很痛苦。
快速参考表
| 方法 | 比较方式 | 典型场景 |
|---|---|---|
| assertEquals | equals() | 值比较,字符串、数字、自定义对象 |
| assertNotEquals | equals() | 确认值不相等 |
| assertTrue / assertFalse | 布尔表达式 | 条件判断,状态验证 |
| assertNull / assertNotNull | == null / != null | 空值检查,数据库查询 |
| assertSame / assertNotSame | == | 单例、缓存、对象引用验证 |
| assertArrayEquals | 逐个元素 equals() | 数组、集合转数组后比较 |
说句实在话:掌握上面这几种,日常写测试基本够用了。但有个误区我得提醒你——每个测试只测一个概念,不要在一个方法里写五六个断言。那样一旦失败,你都不知道哪个条件先炸的。JUnit 5 的 assertAll 可以缓解这个问题,不过那是后话。
好了,断言这块就聊到这儿。记住:选对方法比写对代码更重要。下次写测试时,多想想你真正想验证的是什么——值相等?引用相等?还是非空?选对了,测试代码自己就会说话。