一、测试基础与Kotlin测试环境搭建
各位同学好,我是你们的讲师。今天咱们聊聊测试这件事。说实话,我见过太多开发者把测试当成「可有可无」的环节,结果上线后被打脸。我自己也吃过这个亏——有一次发布版本,因为一个边界条件没测到,导致用户数据丢失,那叫一个惨。
所以这门课,我会把我在实际项目中踩过的坑、总结的经验,全都掏出来给你。咱们从最基础的东西开始。
1.1 软件测试金字塔
先看一张图,这是我每次带团队都要画的。
这个金字塔什么意思呢?说白了就是:底层单元测试要多写,上层UI测试要少写。我见过不少团队,上来就搞UI自动化测试,结果维护成本高得吓人,最后全废弃了。
核心原则:
- 单元测试:覆盖业务逻辑、工具类、数据层
- 集成测试:验证模块间交互、数据库、网络
- UI测试:只覆盖关键用户流程
1.2 单元测试 vs UI测试
这两者有什么区别?我直接给你列个表,一看就明白。
| 对比维度 | 单元测试 | UI测试 |
|---|---|---|
| 测试对象 | 单个函数/类 | 整个界面交互 |
| 运行速度 | 毫秒级 | 秒级甚至分钟级 |
| 依赖环境 | 纯JVM即可 | 需要模拟器/真机 |
| 稳定性 | 高 | 低(受系统影响) |
| 定位问题 | 精准到行 | 只能定位到界面 |
你想想看,如果每个UI测试都要等模拟器启动、页面加载,那得多浪费时间?所以我个人的习惯是:能用单元测试解决的问题,绝不上UI测试。
1.3 Kotlin测试框架概览
Kotlin的测试生态其实很成熟。我常用的就这三个:
JUnit 5
这是Java/Kotlin测试的基石。说白了,它就是一套「测试规范」——告诉你怎么写测试、怎么运行测试。Kotlin对JUnit的支持非常好,可以直接用lambda表达式写断言。
// 一个简单的JUnit测试
@Test
fun `加法应该返回正确结果`() {
val result = 1 + 1
assertEquals(2, result)
}
Mockito
Mockito用来做「模拟」。什么意思呢?比如你的ViewModel依赖一个网络接口,但你不想真的去请求网络,那就用Mockito模拟这个接口的返回值。
我的经验:Mockito配合Kotlin的mockito-kotlin扩展库,写起来特别顺手。我曾经用Mockito模拟了一个复杂的数据库操作,省去了搭建测试环境的麻烦。
// 使用Mockito模拟依赖
val mockRepo = mock<UserRepository>()
`when`(mockRepo.getUserName()).thenReturn("张三")
val viewModel = UserViewModel(mockRepo)
assertEquals("张三", viewModel.userName)
Espresso
Espresso是Google官方的UI测试框架。它可以直接操作界面上的控件,模拟点击、输入等操作。不过要注意,Espresso测试只能在Android设备或模拟器上运行。
// Espresso UI测试示例
@Test
fun `点击登录按钮应该跳转主页`() {
onView(withId(R.id.btn_login)).perform(click())
onView(withId(R.id.tv_welcome)).check(matches(isDisplayed()))
}
1.4 在Android Studio中配置测试环境
这部分我手把手带你走一遍。其实配置很简单,但有几个坑要注意。
第一步:检查依赖
打开你的build.gradle.kts(模块级别),确保有以下依赖:
dependencies {
// 单元测试
testImplementation("junit:junit:4.13.2")
testImplementation("org.mockito:mockito-core:5.3.1")
testImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0")
// UI测试
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation("androidx.test:runner:1.5.2")
}
注意:我曾经遇到过一个问题——mockito-kotlin和mockito-core版本不兼容,导致运行时崩溃。建议统一使用最新稳定版。
第二步:创建测试目录
Android Studio默认会生成两个测试目录:
src/test/java/— 放单元测试(运行在JVM上)src/androidTest/java/— 放UI测试(运行在设备上)
别搞混了。我见过有人把UI测试写在test目录下,结果跑不起来。
第三步:配置测试运行器
在defaultConfig里加上:
android {
defaultConfig {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
}
1.5 创建第一个测试用例
好了,理论说完了,咱们动手写一个。我选一个最简单的场景——测试一个计算器工具类。
被测试的代码
// Calculator.kt
class Calculator {
fun add(a: Int, b: Int): Int = a + b
fun divide(a: Int, b: Int): Int {
if (b == 0) throw IllegalArgumentException("除数不能为0")
return a / b
}
}
编写单元测试
// CalculatorTest.kt
import org.junit.Test
import org.junit.Assert.*
class CalculatorTest {
private val calculator = Calculator()
@Test
fun `加法_正数相加_返回正确结果`() {
val result = calculator.add(3, 5)
assertEquals(8, result)
}
@Test
fun `加法_负数相加_返回正确结果`() {
val result = calculator.add(-1, -2)
assertEquals(-3, result)
}
@Test(expected = IllegalArgumentException::class)
fun `除法_除数为0_抛出异常`() {
calculator.divide(10, 0)
}
@Test
fun `除法_正常除法_返回正确结果`() {
val result = calculator.divide(10, 2)
assertEquals(5, result)
}
}
小技巧:Kotlin允许在函数名中使用反引号,这样测试方法名可以写成自然语言,可读性特别好。我个人非常推荐这种写法。
运行测试
在Android Studio中,右键点击测试类或方法,选择「Run」即可。你会看到绿色的勾,表示测试通过。
嗯,到这里,你的第一个测试用例就跑起来了。是不是很简单?
小结
这一章我们聊了测试金字塔、单元测试和UI测试的区别,也介绍了JUnit、Mockito、Espresso这三个框架。最后我们亲手写了一个测试用例。记住:测试不是负担,而是保障。我见过太多项目因为缺少测试,改一个功能崩一片。从今天开始,养成写测试的习惯。
公众号:蓝海资料掘金营,微信deep3321