什么是 Mock(模拟)?
模拟是一种创建“假的”对象或系统的过程,用来模仿真实对象的行为。这样,开发者可以在不依赖外部系统的情况下,单独测试自己的代码逻辑。模拟可以让测试更快,因为不需要连接真实的数据库或网络接口等。
模拟的优点
MockK 简介
MockK 是专门为 Kotlin 设计的强大灵活的模拟库,支持 Kotlin 特有的功能(如协程、扩展函数),提供简洁易用的接口,社区活跃,维护良好。
如何添加 MockK 依赖(Gradle)
testImplementation "io.mockk:mockk:1.13.4"
(你可以去官网查看最新版本)
MockK 示例:模拟服务器请求
假设你有一个 WeatherService
类负责从外部API获取天气数据,你可以用 MockK 模拟 API 客户端接口 ApiClient
,从而只测试 WeatherService
自己的逻辑。
// 数据类,代表天气信息
data class Weather(val condition: String, val temperature: Int)
// API 客户端接口
interface ApiClient {
fun fetchWeather(city: String): Weather
}
// 天气服务类,依赖 ApiClient
class WeatherService(private val apiClient: ApiClient) {
fun getWeather(city: String): Weather {
return apiClient.fetchWeather(city)
}
}
// 测试类,使用 MockK 模拟 ApiClient
class WeatherServiceTest {
private val mockApiClient = mockk<ApiClient>() // 创建 ApiClient 的模拟对象
private val weatherService = WeatherService(mockApiClient) // 把模拟对象注入 WeatherService
@Test
fun `test getWeather returns correct weather data`() {
val city = "New York"
val expectedWeather = Weather("Sunny", 25)
every { mockApiClient.fetchWeather(city) } returns expectedWeather // 设置模拟行为
val actualWeather = weatherService.getWeather(city)
assertEquals(expectedWeather, actualWeather) // 断言结果符合预期
verify { mockApiClient.fetchWeather(city) } // 验证方法被调用过
}
}
参数匹配器(Argument Matchers)
MockK 可以灵活匹配方法调用的参数:
every { myMock.myMethod(eq("foo")) } returns 42
every { myMock.myMethod(any()) } returns 42
every { myMock.myMethod(isNull<String>()) } returns 42
every { myMock.myMethod(range(10, 20)) } returns 42
- match(自定义条件):匹配符合自定义条件的参数,比如集合
every { myMock.myMethod(match { it.contains("foo") }) } returns 42
every { myMock.myMethod(any()) } throws RuntimeException("错误")
justRun { myMock.myMethod(any()) }
Spy(间谍)
Spy 是对真实对象的包装,可以调用真实方法,也可以验证或覆盖部分方法行为。
val realService = WeatherService()
val spyService = spyk(realService)
val weather = spyService.fetchWeather("New York")
verify { spyService.fetchWeather("New York") }
assertEquals("Sunny in New York", weather)
注解用法
MockK 支持用注解简化模拟对象创建:
示例:
@ExtendWith(MockKExtension::class)
class WeatherServiceTest {
@MockK
private lateinit var mockApiClient: ApiClient
@InjectMockKs
private lateinit var weatherService: WeatherService
@Test
fun testGetWeather() {
// ...
}
}
对象模拟(Object Mocking)
Kotlin 的对象(单例)也可以用 mockkObject
来模拟:
object WeatherService {
fun fetchWeather(city: String) = "Sunny in $city"
}
@Test
fun testMockObject() {
mockkObject(WeatherService)
every { WeatherService.fetchWeather("New York") } returns "Cloudy"
val weather = WeatherService.fetchWeather("New York")
verify { WeatherService.fetchWeather("New York") }
assertEquals("Cloudy", weather)
unmockkObject(WeatherService) // 结束后恢复
}
关键字和含义总结
spyk() | 创建一个 Spy,包裹真实对象,可以调用真实方法 |
justRun { } | 用于模拟返回 Unit(无返回值)的方法 |
mockkObject | 模拟 Kotlin 中的单例对象 |
unmockkObject | 解除对单例对象的模拟,避免影响其他测试 |
@InjectMockKs | 注解,将 Mock 对象自动注入到被测对象 |
|
---|