MockK: что это, ключевые понятия и синтаксис

Автор: IT Sectr Опубликовано: 2026-04-08 Время чтения: 8 мин

MockK — это Kotlin-first фреймворк для создания mock-объектов, разработанный специально для экосистемы Kotlin с учётом её языковых особенностей: корутин, extension-функций, data class и sealed class. В отличие от Mockito, который портирован на Kotlin из Java, MockK изначально проектировался под Kotlin-синтаксис и не требует дополнительных плагинов для работы с final-классами. По данным MockK.io, библиотека используется более чем в 40% Kotlin-проектов с модульным тестированием.

Главное

  • MockK — Kotlin-ориентированная библиотека для мокирования с поддержкой корутин и фич языка.
  • mockk() — основной метод создания мок-объекта, аналогичный Mockito.mock().
  • every { } — блок для настройки поведения мока (stubbing) в декларативном стиле.
  • coEvery / coVerify — специальные конструкции для работы с suspend-функциями корутин.
  • Relaxed mock — мок, который возвращает значения по умолчанию без явного стаббинга.

Что такое MockK?

MockK — это библиотека для создания mock-объектов, написанная на Kotlin и оптимизированная под его синтаксис. Она решает те же задачи, что и Mockito — изоляция тестируемого кода от зависимостей — но делает это с использованием Kotlin-specific конструкций: лямбд, DSL, reified generics и suspend-функций.

Основное преимущество MockK перед портированными решениями — нативная поддержка Kotlin. В Mockito мокирование final-класса требует opt-in (mockito-inline), а статических методов — mockStatic. MockK поддерживает это по умолчанию, поскольку Kotlin-классы по умолчанию final, и обход этого ограничения встроен в архитектуру библиотеки.

Версия 1.13.12 (2024) — стабильный релиз, поддерживающий Kotlin 2.0, K2-компилятор и мультиплатформенные проекты (KMP). MockK также работает с Kotlin/Native и Kotlin/JS, что делает его единственным выбором для KMP-проектов, где ни Mockito, ни EasyMock не применимы.

MockK спроектирован с учётом специфики Kotlin и использует языковые возможности — reified generics, DSL с лямбдами, inline-функции — для обеспечения лаконичного и типобезопасного API без потери производительности.

Как работает MockK

Механизм MockK основан на байткод-инструментации через библиотеку ByteBuddy (как и Mockito), но оборачивает её в Kotlin-friendly DSL. Вместо цепочек when().thenReturn() MockK использует лямбда-блоки every { } и coEvery { }, которые выглядят как естественное расширение языка. Под капотом MockK перехватывает вызов внутри лямбды, анализирует метод и аргументы через рефлексию и сопоставляет с записанными правилами стаббинга.

Базовый синтаксис MockK

Блок every { mock.method() } returns value читается как «каждый раз, когда вызывается метод, возвращать значение». Такой декларативный синтаксис ближе к Kotlin-стилю и исключает путаницу с порядком аргументов в when(). Благодаря reified generics Kotlin, тип мока выводится автоматически без явного указания класса.

kotlin
val repository = mockk<UserRepository>()

// Stubbing: каждый вызов findById(1) возвращает пользователя
every { repository.findById(1) } returns User("Alice")

// Вызов и проверка
val result = repository.findById(1)
assertEquals("Alice", result.name)

Relaxed mock: меньше boilerplate

В отличие от Mockito, где каждый метод нужно настраивать явно, MockK поддерживает relaxed mock — мок, который возвращает «разумные» значения по умолчанию для любого метода: пустой список для List, 0 для Int, пустую строку для String. Это резко сокращает объём подготовительного кода.

kotlin
// Relaxed mock — все методы возвращают значения по умолчанию
val api = mockk<ApiService>(relaxed = true)

// Не требует стаббинга — вернёт пустой список
println(api.getUsers()) // []

Создание моков и релаксированных моков

MockK предлагает несколько способов создания мок-объектов: mockk<T>() для строгого мока (каждый метод должен быть явно настроен), mockk<T>(relaxed = true) для релаксированного мока и spyk(obj) для создания шпиона на реальном объекте.

ФункцияТипПоведение без стаббинга
mockk()Строгий мокБросает исключение при вызове не-стабнутого метода
mockk(relaxed = true)Релаксированный мокВозвращает значение по умолчанию
spyk()ШпионВызывает реальный метод, если не настроен стаб
slot()Argument CaptorЗахватывает аргумент для проверки

Выбор между строгим и релаксированным моком зависит от контекста. Строгий мок гарантирует, что тест не использует методы, поведение которых не определено — это повышает надёжность. Релаксированный мок удобен для быстрого прототипирования тестов, где не важны все зависимости. На практике рекомендуется начинать со строгого мока и переключаться на relaxed только когда стаббинг занимает больше строк, чем сам тест.

Stubbing: настройка поведения с every блоком

Блок every — это центральная конструкция стаббинга в MockK. Внутри лямбды описывается вызов метода с конкретными аргументами, а после возвращается значение через returns, выбрасывается исключение через throws или вычисляется ответ через answers.

Различные способы стаббинга

MockK поддерживает все сценарии, необходимые для тестирования: возврат значения, выброс исключения, вычисление ответа на основе аргументов, несколько ответов по порядку (последовательность вызовов).

kotlin
// Возврат значения
every { repo.findById(1) } returns User("Alice")

// Выброс исключения
every { repo.findById(999) } throws NotFoundException()

// Динамический ответ
every { repo.save(any()) } answers {
    val user = firstArg<User>()
    user.copy(id = 42)
}

// Последовательность ответов
every { repo.findAll() } returnsMany listOf(
    listOf(User("Alice")),
    listOf(User("Bob")),
    emptyList()
)

Verify и coVerify для корутин

Verify в MockK аналогичен Mockito.verify() по смыслу, но использует Kotlin DSL: verify { mock.method() }. Для suspend-функций используется coVerify { mock.suspendMethod() }, который корректно работает с корутинами и не требует специального раннера.

Проверка количества вызовов

MockK поддерживает те же модификаторы, что и Mockito: exactly(1), atLeast(2), atMost(5), wasNot(Called). Синтаксис минималистичный — модификатор передаётся первым аргументом в verify { }.

kotlin
// Проверка: метод вызван ровно 1 раз
verify(exactly = 1) { repo.save(any()) }

// Проверка порядка вызовов
verifySequence {
    repo.save(any())
    repo.flush()
}

// coVerify для suspend-функций
coVerify { api.fetchUsers() }

Slot: захват аргументов

Для проверки аргументов используется slot() — аналог ArgumentCaptor. Slot объявляется до вызова, передаётся в every или verify, и после выполнения теста содержит захваченное значение.

kotlin
val userSlot = slot<User>()

verify { repo.save(capture(userSlot)) }

assertEquals("Alice", userSlot.captured.name)

Аннотации MockK и JUnit интеграция

MockK предоставляет аннотации @MockK и @RelaxedMockK для создания моков через инициализацию в JUnit 5. Расширение MockKExtension автоматически создаёт моки перед каждым тестом и очищает после — аналогично MockitoExtension, но с поддержкой relaxed-режима.

Пример с MockKExtension

Аннотация @InjectMockKs (или альтернатива @MockK с явным созданием объекта) внедряет моки в тестируемый экземпляр. Это сокращает boilerplate и делает код теста чище.

kotlin
@ExtendWith(MockKExtension::class)
class UserServiceTest {

    @MockK
    lateinit var repository: UserRepository

    @InjectMockKs
    lateinit var service: UserService

    @Test
    fun `getUser returns user from repository`() {
        every { repository.findById(1) } returns User("Alice")
        assertEquals("Alice", service.getUser(1)?.name)
    }
}

MockK vs Mockito: что выбрать для Kotlin

Выбор между MockK и Mockito зависит от состава команды и типа проекта. Mockito имеет большую экосистему, больше примеров и интеграций, но MockK предоставляет более чистый Kotlin-синтаксис и нативную поддержку языковых фич. Для новых Kotlin-проектов MockK рекомендуется как более идиоматичное решение.

КритерийMockKMockito
СинтаксисKotlin DSL (every, verify)Java-стиль (when, thenReturn)
КорутиныcoEvery, coVerify (нативно)Требует доп. библиотек
Final classПоддерживается по умолчаниюТребует mockito-inline
KMPПоддерживаетсяНе поддерживается
Relaxed mockВстроенНет аналога
ПопулярностьРастущая в Kotlin-сообществеДоминирует в Java и гибридных проектах

Для проектов на чистом Kotlin (без Java-классов) MockK предпочтительнее: меньше boilerplate, нативная поддержка корутин, нет сюрпризов с final-классами. Для гибридных проектов или команд с Java-бэкграундом Mockito остаётся рабочим вариантом — обе библиотеки можно использовать в одном проекте через разные модули. При миграции с Mockito на MockK достаточно заменить аннотации @Mock на @MockK и переписать блоки when().thenReturn() в формат every { }.

Часто задаваемые вопросы

Чем relaxed mock отличается от обычного мока в MockK?

Relaxed mock возвращает значения по умолчанию для всех не-стабнутых методов (пустой список, 0, null), не бросая исключений. Обычный (strict) mock требует явного стаббинга каждого метода — иначе тест падает. Relaxed mock удобен для быстрых тестов, strict — для надёжных.

Как мокировать extension-функции в MockK?

MockK поддерживает мокирование extension-функций через mockkStatic(). Это возможно потому, что extension-функции в Kotlin — это статические методы с первым параметром-приёмником. Для каждой extension-функции нужно указать класс, в котором она объявлена.

Работает ли MockK с Kotlin Multiplatform?

Да, MockK поддерживает Kotlin Multiplatform (KMP) для common-кода. На платформах JVM, Native и JS можно использовать общий API mockk(), every, verify. Это делает MockK единственным выбором для KMP-проектов, где Mockito не работает.

Как проверить порядок вызовов в MockK?

Используйте verifySequence { } — блок, в котором вызовы указываются строго в ожидаемом порядке. Если реальный порядок отличается, verifySequence бросит исключение с указанием первого несовпавшего вызова.

Можно ли использовать MockK и Mockito в одном проекте?

Да, технически это возможно, но не рекомендуется. Конфликты могут возникнуть на уровне байткод-инструментации (ByteBuddy vs mockito-inline). Если проект уже использует Mockito, миграция на MockK может быть постепенной через изоляцию модулей.

Итоги

  • MockK — Kotlin-first библиотека для мокирования с нативной поддержкой языка.
  • every { } — декларативный DSL для настройки поведения моков.
  • coEvery / coVerify — поддержка suspend-функций корутин без дополнительных зависимостей.
  • Relaxed mock — мок со значениями по умолчанию, сокращающий boilerplate.
  • @MockK / @InjectMockKs — аннотации для автоматического создания моков в JUnit 5.
  • MockK vs Mockito — MockK предпочтительнее для чистых Kotlin-проектов и KMP.
  • verifySequence — проверка строгого порядка вызовов методов.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также