Testing in Kotlin: unit, integration, e2e from A to Z

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to Testing in Kotlin: From Modules to User Experience

Kotlin is a modern language actively used for developing server-side (Ktor, Spring Boot), Android applications, and multiplatform projects. However, code quality is impossible without proper testing. In this article, we will cover three key levels: unit, integration, and e2e (end-to-end) testing. You will learn which tools to use, how to write tests in Kotlin, and how to build a reliable testing strategy for your application.



1. Unit Testing in Kotlin: The Foundation of Stability

Unit tests verify individual functions, classes, or methods in isolation from external dependencies. In Kotlin, JUnit 5 and MockK are most commonly used for this.



Tools and Setup

Add dependencies to build.gradle.kts:

dependencies {    testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")    testImplementation("io.mockk:mockk:1.13.8")}


Unit Test Example with MockK

Suppose we have a service that depends on a repository:

class UserService(private val repo: UserRepository) {    fun getUserName(id: Int): String {        val user = repo.findById(id)        return user?.name ?: "Unknown"    }}

Test using MockK:

import io.mockk.everyimport io.mockk.mockkimport org.junit.jupiter.api.Assertions.assertEqualsimport org.junit.jupiter.api.Test

class UserServiceTest { @Test fun `getUserName returns name when user exists`() { val mockRepo = mockk<UserRepository>() every { mockRepo.findById(1) } returns User(1, "Alice") val service = UserService(mockRepo) assertEquals("Alice", service.getUserName(1)) }}


Best Practices for Unit Tests

  • One test — one assertion (use assert wisely).
  • Isolate dependencies: use mocks (MockK) or stubs.
  • Name tests clearly: shouldReturnX_whenY.
  • Test edge cases: null, empty lists, exceptions.


2. Integration Testing: Verifying Component Interaction

Integration tests verify how real components work together: database, API, file system. In Kotlin, Testcontainers and Ktor Client (if you are writing a server) are convenient for this.



Example: Testing a Repository with PostgreSQL

Add Testcontainers and exposed (a library for working with databases):

dependencies {    testImplementation("org.testcontainers:postgresql:1.19.3")    implementation("org.jetbrains.exposed:exposed:0.44.1")}

Test for UserRepository:

import org.junit.jupiter.api.*import org.testcontainers.containers.PostgreSQLContainerimport org.testcontainers.junit.jupiter.Containerimport org.testcontainers.junit.jupiter.Testcontainersimport org.jetbrains.exposed.sql.*

@Testcontainersclass UserRepositoryIntegrationTest {

@Container val postgres = PostgreSQLContainer<Nothing>("postgres:15").apply { withDatabaseName("testdb") withUsername("test") withPassword("test") }

private lateinit var repo: UserRepository

@BeforeEach fun setUp() { Database.connect(postgres.jdbcUrl, driver = "org.postgresql.Driver", user = postgres.username, password = postgres.password) transaction { SchemaUtils.create(Users) } repo = UserRepository() }

@Test fun `save and find user by id`() { val user = User(1, "Bob") repo.save(user) val found = repo.findById(1) assertEquals("Bob", found?.name) }

@AfterEa

Blogs

Book Recommendations