编写第一个测试:@Test 注解与基本断言
我们要从零开始写一个真正的测试了。你可能会想:「不就是加个注解嘛,有什么好讲的?」
嗯,我当年也这么觉得。直到有一次我在 CI 上跑测试,发现一个测试方法根本没执行,排查了半小时才发现——我忘了加 @Test。所以别看它简单,细节里全是坑。
1. @Test 注解——测试的入口
@Test 来自 kotlin.test 包,它告诉测试框架「这个方法是一个测试用例」。被标注的方法必须满足三个条件:
- 返回值类型必须是
Unit(也就是不返回任何东西) - 不能有参数
- 方法名不能是
test开头?——其实没有这个限制,但按惯例我们通常写成testXxx或shouldXxx
import kotlin.test.Test
class CalculatorTest {
@Test
fun testAddition() {
// 这里写测试逻辑
}
}
如果你用 IntelliJ IDEA,直接在类名或方法名左侧点击绿色箭头就能运行。我个人习惯给测试类名加一个
Test 后缀,统一好找。2. 基本断言——让测试「说话」
光有 @Test 还不够,你得告诉框架「预期结果是什么」。断言就是干这个活的。Kotlin 的 kotlin.test 提供了几个常用函数,我列个表格:
| 断言函数 | 作用 | 示例 |
|---|---|---|
assertEquals(expected, actual) |
判断两个值相等(通过 equals 比较) |
assertEquals(4, calculator.add(2, 2)) |
assertNotEquals(illegal, actual) |
判断两个值不相等 | assertNotEquals(5, calculator.add(2, 2)) |
assertTrue(condition) |
判断条件为 true |
assertTrue(list.isEmpty()) |
assertFalse(condition) |
判断条件为 false |
assertFalse(list.isNotEmpty()) |
assertNull(value) |
判断值为 null |
assertNull(cache.get("not-exist")) |
assertNotNull(value) |
判断值非 null |
assertNotNull(viewModel.user) |
assertSame(expected, actual) |
判断两个引用指向同一个对象(引用比较) | assertSame(singleton, Singleton.instance) |
大部分情况下 assertEquals + assertTrue 已经能覆盖 90% 的场景。我建议你优先用 assertEquals,因为它失败时能打印出期望值和实际值,调试起来很方便。
3. 写一个完整的测试
我们拿一个简单的减法函数来练手:
class Calculator {
fun subtract(a: Int, b: Int): Int {
return a - b
}
}
// 测试类
class CalculatorTest {
private val calculator = Calculator()
@Test
fun testSubtract() {
val result = calculator.subtract(10, 3)
assertEquals(7, result)
}
@Test
fun testSubtractNegativeResult() {
val result = calculator.subtract(3, 10)
assertEquals(-7, result)
}
@Test
fun testSubtractWithZero() {
val result = calculator.subtract(5, 0)
assertEquals(5, result)
}
}
运行所有测试,如果全部通过你会看到绿色提示。有一个失败就会显示红色,并且告诉你哪一行、期望值和实际值分别是什么。
我曾经写过一个测试,
assertEquals 里把期望值和实际值写反了——第一个参数写成了实际值。虽然结果一样,但报错信息会完全反着显示,排查起来非常痛苦。所以麻烦你养成习惯:第一个参数写期望值,第二个参数写实际值。4. 知识结构一览
下面这张图把整个流程串了起来,你可以把它当作大脑地图:
你跟着这个流程走一遍,基本就能写出第一个能用的测试。注意每个节点都有对应的代码实现,没什么黑魔法。
5. 额外的小贴士
- 测试方法名要能表达意图:比如
shouldReturnSevenWhenSubtractTenMinusThree虽然长,但失败时一眼就能看出哪里逻辑错了。 - 一个测试方法只测一个逻辑点:不要在同一个
@Test方法里断言好几个不相关的场景,否则第一个失败后面就全跳过了。 - 及时清理测试数据:如果测试修改了全局状态,记得在
@After里恢复。不过那是后面章节的内容了,今天先不用操心。
小结一下:
@Test 负责标记,「断言」负责验证。两者结合就是最基础的测试骨架。你可以在任何 Kotlin 项目中用同样的模式开始写测试,不管它是不是回归测试。