Mocks and Fixtures in Kotlin: A Painless Testing Guide

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: Why tests without mocks are like coffee without caffeine?

\n\n

Hello, colleague! If you write in Kotlin and want your code not to break on every other commit, sooner or later you'll come to tests. And in tests — to mock objects and fixtures. Without them, unit tests turn into integration tests: you're not testing a single class, but a whole chain of dependencies. And if the database goes down or the API returns a 500 — the test will fail, even though your code might be perfect.

\n\n

In this article, we'll look at how to properly use Mock objects and fixtures in Kotlin tests so that tests are fast, reliable, and tell you the truth, not "everything is broken, fix it." Let's go!

\n\n

What is a mock and why do you need it in Kotlin?

\n\n

A mock object is an imitation of a real object. Instead of spinning up an entire server or database, we create a "stub" that behaves the way we tell it to. This allows you to isolate the code under test from the outside world.

\n\n

Basic concepts

\n
    \n
  • Mock — an object whose method behavior we control. We can specify what it returns and verify whether it was called.
  • \n
  • Stub — a special case of a mock that only returns predefined values. It does not verify calls.
  • \n
  • Fixture — a set of pre-prepared data or states needed to run a test. For example, a test user or database settings.
  • \n
\n\n

In the Kotlin world, there are two main players: MockK (native to Kotlin) and Mockito (popular in Java). We will use MockK — it works better with coroutines, extension functions, and sealed classes.

\n\n

Project setup: adding MockK and JUnit 5

\n\n

First, let's add the dependencies to build.gradle.kts:

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

Now we can write tests. Let's create a simple service and test it using mocks.

\n\n

First example: mocking a repository

\n\n

Imagine we have a UserService that depends on UserRepository:

\n\n
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

Now let's write a test using a mock object for the repository:

\n\n
import 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        // Arrange\n        every { repository.findById(1L) } returns User(1L, "Alice")\n\n        // Act\n        val name = service.getUserName(1L)\n\n        // Assert\n        assertEquals("Alice", name)\n        verify { repository.findById(1L) }\n    }\n\n    @Test\n    fun `should throw exception when user not found`() {\n        // Arrange\n        every { repository.findById(any()) } returns null\n\n        // Act & Assert\n        assertFailsWith<IllegalArgumentException> {\n            service.getUserName(99L)\n        }\n    }\n}\n
\n\n

Notice: every sets the mock's behavior, verify checks that the method was called. This is the foundation of mock objects and fixtures in Kotlin tests.

\n\n

Fixtures: preparing data in advance

\n\n

When there are many tests, duplicating data creation is bad practice. Fixtures come to the rescue. In Kotlin, they are conveniently

Blogs

Book Recommendations