常用断言函数:assertEquals、assertTrue、assertFalse、assertNull

写回归测试,说白了就是不断验证预期结果。你写了一堆kotlin.test的断言,能不能一次跑通、一眼定位问题,全靠这几个常用函数撑腰。
我这些年带团队写测试,发现最容易被忽略的恰恰是这些基础断言。别笑,很多人连assertEquals和===的区别都说不清。那咱们今天就把它彻底掰开揉碎讲清楚。

1. assertEquals:值相等性判断

这是曝光率最高的断言,没有之一。
它判断的不是引用相等(即===),而是结构相等(即==)。

@Test
fun testUser() {
    val expected = User("张三", 28)
    val actual = User("张三", 28)
    assertEquals(expected, actual)  // 通过,因为 data class 自动重写了 equals
}

我遇到过好几次这种情况:明明两个对象的内容一模一样,但用assertSame(即===)断言却挂了。后来一看,是因为两个对象是不同实例。
所以记住:业务价值比较用assertEquals,引用比较用assertSame。

小提示:assertEquals支持一个可选第三个参数——失败时的消息。我个人习惯把这个消息写成“期望是什么,实际是什么”的模板,这样CI日志里一眼就能看出问题。

assertEquals(42, result, "结果应该是 42,但实际是 $result")

2. assertTrue / assertFalse:布尔条件验证

这两个函数专门用来验证某个条件是否为真或假。拿它们来处理复杂的业务逻辑断言特别好使。

@Test
fun testDiscount() {
    val order = Order(amount = 200)
    val eligible = order.isMember() && order.amount > 100
    assertTrue(eligible, "会员且金额大于100应该享受折扣")
}

你可能会想:为什么不用assertEquals(true, eligible)?
嗯,这里有个小细节——assertTrue失败时打印的信息更友好,而且语义上更明确。
我建议:只要是布尔表达式,就用assertTrue/assertFalse,别偷懒。

举个例子,我曾经在一段支付测试代码里写过这样的断言:

assertFalse(payment.isBlocked(), "VIP用户不应该被拦截")

结果CI上这个断言挂了,排查半天发现是isBlocked()里有副作用。如果用assertEquals(false, ...),我可能根本不会注意到底层逻辑的变化。所以,用对断言就是给自己挖坑铺路。

3. assertNull / assertNotNull:空值检查

Kotlin虽然严格空安全,但数据从网络、数据库或第三方库过来时,依然可能遇到null。这时候就需要它们出场了。

@Test
fun testUserExists() {
    val user = userRepository.findById(999)
    assertNull(user, "不存在的用户ID应该返回null")
}

注意:不要拿assertEquals(null, user)代替assertNull。虽然结果一样,但assertNull在IDE里会提示更清晰的编译时判断,而且失败消息也更友好。

我记得有一次重构缓存逻辑,忘了清空本地缓存。测试里用了assertNotNull,结果始终通过——因为缓存里确实还有旧数据。
后来我改成assertNull才发现问题。所以建议双向覆盖:先断言null,再断言not null,不要跳过任何一个方向。

4. 实战建议与避坑指南

断言函数用途等同于失败信息友好度
assertEquals结构相等a == b⭐⭐⭐⭐
assertTrue/assertFalse布尔条件a == true / false⭐⭐⭐⭐⭐
assertNull/assertNotNull空值检查a == null / a != null⭐⭐⭐⭐
assertSame/assertNotSame引用相等a === b⭐⭐⭐

我建议你在项目里定一个断言选用规范:
- 比较值 → assertEquals
- 比较布尔 → assertTrue/assertFalse
- 检查null → assertNull/assertNotNull
- 比较引用 → 极少用,仅在测试单例或缓存时使用assertSame

避坑指南:
我曾经犯过一个低级错误——在assertEquals里传反了expected和actual的顺序。
这会导致失败消息里“期望”和“实际”刚好颠倒,排查起来非常痛苦。
统一约定:将“期望值”放在第一个参数,“实际值”放在第二个参数。
小组内可以写个lint规则,强制检查参数顺序。

还有一个非常常用的技巧:结合lambda编写自定义断言。但那是进阶用法了,本节我们先把基础打牢。

核心总结:
- 用assertEquals验证业务值相等
- 用assertTrue/assertFalse验证生效条件
- 用assertNull/assertNotNull验证空安全
- 参数顺序不要搞反,时刻带上失败消息

断言函数选择流程图 编写测试断言 需要比较值? assertEquals 否 需要检查布尔条件? assertTrue/assertFalse 否 需要检查空值? 是 assertNull/assertNotNull 否 选对断言事半功倍

写好断言,其实是在给未来的自己铺路。你想想看,如果CI报错,第一个看到的不是“expected: true but was: false”这种模糊信息,而是“VIP用户应该享受折扣:实际金额与预期不符”,定位速度是不是快很多?
我每次写测试都会刻意留出5分钟专门检查断言,看看有没有用对类型、有没有漏掉失败消息。这个习惯救过我多次。

一句老话:断言选得对,调试少掉泪。