引言:没有Mock的测试,就像没有咖啡因的咖啡?
\n\n你好,同行!如果你在用Kotlin写代码,并且希望你的代码不会在每次提交时都崩溃,那么迟早你会接触到测试。而在测试中,就会用到mock对象和测试夹具。没有它们,单元测试就会变成集成测试:你测试的不是一个类,而是一整条依赖链。如果数据库宕机或API返回500错误,测试就会失败,尽管你的代码可能本身是完美的。
\n\n在本文中,我们将探讨如何在Kotlin测试中正确使用Mock对象和测试夹具,使测试快速、可靠,并能告诉你真实的问题所在,而不是一句“全都坏了,去修吧”。我们开始吧!
\n\n什么是Mock,为什么在Kotlin中需要它?
\n\nMock对象是对真实对象的模拟。我们不必启动整个服务器或数据库,而是创建一个“替身”,它的行为完全由我们控制。这使我们能够将待测试的代码与外部世界隔离开来。
\n\n基本概念
\n- \n
- Mock — 一个我们可以控制其方法行为的对象。我们可以指定它返回什么,并验证它是否被调用过。 \n
- Stub — Mock的一种特殊情况,它只返回预设的值,不验证调用情况。 \n
- Fixture(测试夹具) — 执行测试所需的一组预先准备好的数据或状态。例如,一个测试用户或数据库配置。 \n
在Kotlin世界中,有两个主要工具:MockK(Kotlin原生)和Mockito(在Java中很流行)。我们将使用MockK——它更好地支持协程、扩展函数和密封类。
\n\n项目配置:添加MockK和JUnit 5
\n\n首先,在build.gradle.kts中添加依赖:
dependencies {\n testImplementation("io.mockk:mockk:1.13.8")\n testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")\n}\n\n\n现在可以编写测试了。我们创建一个简单的服务,并用mock来测试它。
\n\n第一个示例:模拟Repository
\n\n假设我们有一个UserService,它依赖于UserRepository:
class UserService(private val repository: UserRepository) {\n fun getUserName(id: Long): String {\n val user = repository.findById(id)\n ?: throw IllegalArgumentException("User not found")\n return user.name\n }\n}\n\n\n现在,我们使用mock对象来模拟Repository,编写测试:
\n\nimport io.mockk.every\nimport io.mockk.mockk\nimport io.mockk.verify\nimport org.junit.jupiter.api.Test\nimport kotlin.test.assertEquals\nimport kotlin.test.assertFailsWith\n\nclass UserServiceTest {\n\n private val repository = mockk<UserRepository>()\n private val service = UserService(repository)\n\n @Test\n fun `should return user name when user exists`() {\n // 准备\n every { repository.findById(1L) } returns User(1L, "Alice")\n\n // 执行\n val name = service.getUserName(1L)\n\n // 断言\n assertEquals("Alice", name)\n verify { repository.findById(1L) }\n }\n\n @Test\n fun `should throw exception when user not found`() {\n // 准备\n every { repository.findById(any()) } returns null\n\n // 执行 & 断言\n assertFailsWith<IllegalArgumentException> {\n service.getUserName(99L)\n }\n }\n}\n\n\n请注意:every用于定义mock的行为,verify用于验证方法是否被调用。这是Kotlin测试中Mock对象和测试夹具的基础。
测试夹具:提前准备数据
\n\n当测试很多时,重复创建数据是一种不好的实践。这时测试夹具就派上用场了。在Kotlin中,可以方便地