Kotlin 测试:单元测试、集成测试、端到端测试高级水平

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么高级测试是可靠 Kotlin 代码的基础

在现代 Kotlin 开发中,编写代码只是工作的一半。真正的精通体现在创建可靠、可测试且易于维护的系统上。基本的单元测试每个人都熟悉,但当项目发展到微服务架构或复杂业务逻辑时,简单的检查就不够了。高级测试不仅包括类的隔离,还包括组件间交互的验证(集成测试)以及用户场景的完整复现(端到端测试)。

本文面向已经熟悉 Kotlin 基础知识并希望将项目质量提升到新水平的开发者。我们将使用现代工具:MockK、Testcontainers 和 Ktor Client,剖析测试的三个关键层级:单元测试、集成测试和端到端测试。您将学习如何编写不仅仅是“通过”的测试,而是真正保护您的代码免受回归影响的测试。



1. 单元测试:使用 MockK 进行隔离和模拟

单元测试是测试金字塔的基础。在高级层面,我们不仅检查函数,还使用模拟对象测试对象之间的复杂交互。在 Kotlin 中,最好的库是 MockK。



1.1. MockK 的高级功能

与 Mockito 不同,MockK 是专门为 Kotlin 编写的,支持协程、Lambda 表达式和扩展函数。让我们看一个测试使用存储库和协程的服务的示例:

// 待测试的服务
class UserService(private val repo: UserRepository) {
    suspend fun getUserName(id: Long): String {
        val user = repo.findById(id)
        return user?.name ?: "Unknown"
    }
}

// 测试
class UserServiceTest {
    private val repo = mockk<UserRepository>()
    private val service = UserService(repo)

    @Test
    fun `should return user name when user exists`() = runTest {
        // given
        coEvery { repo.findById(1L) } returns User(1, "Alice")

        // when
        val result = service.getUserName(1L)

        // then
        assertEquals("Alice", result)
        coVerify(exactly = 1) { repo.findById(1L) }
    }

    @Test
    fun `should return Unknown when user not found`() = runTest {
        // given
        coEvery { repo.findById(any()) } returns null

        // when
        val result = service.getUserName(99L)

        // then
        assertEquals("Unknown", result)
    }
}

关键点:

  • coEvery 和 coVerify — 用于处理 suspend 函数。
  • mockk() 创建一个空的模拟对象,spyk() 创建一个真实对象的部分模拟。
  • 使用 slots 捕获参数。

1.2. 测试 Lambda 表达式和扩展函数

MockK 允许模拟扩展函数,这对于 Ktor 或 Spring WebFlux 等框架至关重要:

class StringValidator {
    fun String.isValidEmail(): Boolean = this.contains("@")
}

// 测试
@Test
fun `test extension mock`() {
    val validator = mockk<StringValidator>()
    with(validator) {
        every { "test@mail.com".isValidEmail() } returns true
        every { "invalid".isValidEmail() } returns false
    }
    assertTrue("test@mail.com".isValidEmail())
    assertFalse("invalid".isValidEmail())
}

2. 集成测试:数据库和 Testcontainers

集成测试检查您的代码如何与真实依赖项交互:数据库、消息队列、外部 API。这里 Testcontainers 派上了用场——一个在测试期间直接启动 Docker 容器的库。

2.1. 使用 PostgreSQL 配置 Testcontainers

对于 Spring Boot 应用程序,配置如下:

博客

书籍推荐