Тестирование в Kotlin: полный обзор фреймворков и инструментов
Kotlin — язык, который стремительно завоевывает популярность в backend-разработке (Spring Boot, Ktor) и Android. Однако мощный язык требует адекватных инструментов тестирования. В этой статье мы разберем ключевые фреймворки и библиотеки для тестирования Kotlin-приложений: от классического JUnit5 до современных Kotest и MockK. Материал будет полезен как новичкам, так и опытным разработчикам, переходящим с Java.
1. JUnit5 — основа современного тестирования
JUnit5 (Jupiter) — стандарт де-факто для модульного тестирования в Java/Kotlin. В Kotlin он используется с небольшими, но важными особенностями.
- Поддержка лямбд и функций высшего порядка — тесты можно писать более лаконично.
- Аннотации —
@Test,@BeforeEach,@AfterAllработают как в Java. - Assumptions и Assertions — встроенные проверки.
Пример простого теста на JUnit5 с Kotlin:
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class CalculatorTest {
private val calculator = Calculator()
@Test
fun `addition of two numbers should return correct sum`() {
val result = calculator.add(2, 3)
assertEquals(5, result)
}
@Test
fun `should throw exception when dividing by zero`() {
assertThrows<IllegalArgumentException> {
calculator.divide(10, 0)
}
}
}
Важно: Kotlin позволяет использовать пробелы в именах тестовых функций (backticks), что делает названия тестов читаемыми на естественном языке.
2. Kotest — нативный Kotlin-фреймворк
Kotest (ранее KotlinTest) — это наиболее полный и гибкий фреймворк, полностью написанный на Kotlin. Он предлагает несколько стилей написания тестов (строковый, спецификационный, поведенческий) и мощные встроенные матчеры.
Ключевые возможности:
- Property-based testing — автоматическая генерация тестовых данных.
- Data-driven тесты — с таблицами входных данных.
- Мощные assertion-матчеры —
shouldBe,shouldContain,shouldThrowи др. - Поддержка Coroutines — встроенный
testCoroutineScope.
Пример теста в стиле "String Spec":
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldThrow
class UserServiceTest : StringSpec({
val service = UserService()
"should return user by id" {
val user = service.getUser(1)
user.name shouldBe "Alice"
}
"should throw exception for invalid id" {
shouldThrow<NoSuchElementException> {
service.getUser(-1)
}
}
})
Property-based testing в Kotest:
import io.kotest.property.forAll
import io.kotest.property.arbitrary.int
class MathPropertiesTest : StringSpec({
"addition is commutative" {
forAll<Int, Int> { a, b ->
(a + b) == (b + a)
}
}
})
3. MockK — моки для Kotlin
В отличие от Mockito (который тоже можно использовать), MockK полностью адаптирован под особенности Kotlin: поддерживает sealed classes, object, extension functions и coroutines.
Основные возможности:
- Лаконичный DSL для создания моков и стабов.
- Поддержка relax-моков (непроверяемые вызовы).
- Верификация порядка вызовов и количества вызовов.
- Моки для
suspend-функций.
Пример работы с MockK:
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.Test
class OrderServiceTest {
private val paymentGateway = mockk<PaymentGateway>()
private val orderService = OrderService(paymentGateway)
@Test
fun `should process payment successfully`() {
// Стаббинг
every { paymentGateway.charge(any(), 100.0) } returns true
val result = orderService.placeOrder("order123", 100.0)
// Проверка результата
assert(result)
// Верификация вызова
verify(exactly = 1) { paymentGateway.charge("order