4. 编写第一个测试用例:@Test注解与基础断言

说句实话,刚接触 Kotlin 测试时,我最大的困惑就是——到底怎么写一个能跑起来的测试? 其实核心就三样东西:一个注解、两个断言方法。今天咱们就把它们彻底讲透。

4.1 @Test 注解:让函数变身测试

在 Kotlin 里,测试方法不过是一个普通函数,但前面要加上 @Test 注解。嗯,就像给函数贴了个“我是测试”的标签。JUnit 运行时只会执行贴了标签的函数。

核心规则:被 @Test 标记的函数必须用 fun 声明,且不能有参数(参数化测试是另一回事,咱们后面讲)。

举个最简单的例子——

import org.junit.Test
import kotlin.test.*

class MyFirstTest {
    @Test
    fun testAddition() {
        assertEquals(4, 2 + 2)
    }
}

这个测试在 JUnit 4 环境下就能直接运行。你可能注意到了,我直接导入了 kotlin.test.*,这包里的断言函数用起来更顺手。

我的小习惯:测试函数命名尽量用 testXxx 或 `测试什么功能`。我个人偏爱后者,因为反引号里可以写中文,一眼就能看出测试点。不过团队约定优先哈。

4.2 assertTrue:验证“真”条件

assertTrue 说白了就是“你这个表达式必须为 true,不然就给我失败”。我在项目中遇到过最典型的使用场景——检查某个列表不为空:

@Test
fun testListNotEmpty() {
    val list = listOf("apple", "banana")
    assertTrue(list.isNotEmpty())
}

如果你想让失败信息更友好,可以加第二个参数:

assertTrue(list.isNotEmpty(), "列表居然为空了,快检查代码!")

4.3 assertEquals:精确对比两个值

这是用的最多的断言。它判断两个对象是否 相等(通过 equals 方法,不是引用相同)。注意:Kotlin 的 assertEquals 参数顺序是 expected, actual,千万别搞反。为什么?因为失败信息会告诉你“期望是XX,实际是YY”,顺序错了你会被误导。

@Test
fun testStringUppercase() {
    val result = "hello".uppercase()
    assertEquals("HELLO", result)
}

我曾经踩过的坑:老习惯把 actual 放前面,结果某次对比两个长字符串,失败信息显示“期望: abc...,实际: xyz...”,我 debug 了半小时才发现参数写反了。真的,顺序很重要。

4.4 两个断言的对比

断言 用途 失败信息特点 最佳搭档
assertTrue 验证布尔表达式为真 默认只输出“expected true but was false” 自定义消息
assertEquals 验证两个对象相等 输出期望值和实际值 复杂对象请重写 equals

你可能想问:什么时候用 assertTrue?举个现实例子——验证一个函数返回的 Result 是否成功:

@Test
fun testApiResponseSuccess() {
    val response = someApi.call()
    assertTrue(response.isSuccess, "API 调用失败")
}

而当你需要精确比对数值、字符串、对象时,assertEquals 更合适。

4.5 完整的测试用例骨架

我们来写一个综合的例子。假设有一个计算器类,我们要测加法:

class Calculator {
    fun add(a: Int, b: Int): Int = a + b
}

class CalculatorTest {
    private val calc = Calculator()

    @Test
    fun testAddPositiveNumbers() {
        val result = calc.add(3, 5)
        assertEquals(8, result, "3 + 5 应该等于 8")
    }

    @Test
    fun testAddWithZero() {
        assertTrue(calc.add(0, 10) == 10, "加 0 结果应不变")
    }

    @Test
    fun testAddNegativeNumbers() {
        val sum = calc.add(-2, -3)
        assertEquals(-5, sum)
    }
}

注意:每个 @Test 方法都是独立的。JUnit 会为每个测试新建一个测试类实例,所以你不用担心测试之间互相干扰。这特性很好用。

4.6 本章知识点全景图

第 4 章 核心知识结构 编写第一个测试用例 @Test 注解 assertTrue assertEquals • 标记测试函数 • 无参数 无返回值 • 验证布尔表达式 • 可带自定义失败消息 • 验证期望值==实际值 • 注意参数顺序 使用原则 能明确比较值就用 assertEquals,只需检查真假就用 assertTrue

4.7 写给初学者的几条提醒

  • 不要在一个测试里写多个断言——虽然技术上允许,但第一个断言失败后后面的代码就不跑了,你无法知道后续是否也有 bug。我建议一个测试只验证一个行为。
  • 善用描述性函数名:比如 testDepositIncreasesBalance,失败了你能从名字大概猜到原因。
  • 运行测试:IntelliJ 里可以直接点方法名左侧的绿色三角,也可以右键类名运行整个测试类。试试看?