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-ориентирана библиотека за mockване с подкрепа за корутини и езикови функции.
  • mockk() — основният метод за създаване на mock-обект, аналогичен на Mockito.mock().
  • every { } — блок за конфигуриране на поведението на mock (stubbing) в декларативен стил.
  • coEvery / coVerify — специални конструкции за работа с suspend функции на корутините.
  • Relaxed mock — mock, който връща подразбираеми стойности без изрично stubbing.

Какво е MockK?

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

Основното предимство на MockK преди пренесените решения — воденствената подкрепа за Kotlin. В Mockito, mockването на 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-приятелски DSL. Вместо вериги от when().thenReturn(), MockK използва ламбда блокове every { } и coEvery { }, които изглеждат като естествено разширение на езика. Под капака, MockK прехварля повикането внутри на ламбдата, анализира метода и аргументите чрез рефлексия и ги съпоставя с записаните правила на stubbing.

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

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

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 — mock, който връща „разумни” подразбираеми стойности за всякакъв метод: празен списък за List, 0 за Int, празен низ за String. Това драстично намалява количеството на подготвителен код.

kotlin
// Relaxed mock — всички методи връщат подразбираеми стойности
val api = mockk<ApiService>(relaxed = true)

// Не изисква stubbing — ще връне празен списък
println(api.getUsers()) // []

Създаване на mockове и релаксирани mockове

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

ФункцияТипПоведение без stubbing
mockk()Строг mockХвърля изключение при повикане на нестубнат метод
mockk(relaxed = true)Релаксиран mockВръща подразбираема стойност
spyk()ШпионПовика реалния метод, ако няма конфигуриран stub
slot()Argument CaptorУлавя аргумента за проверка

Изборът между строг и релаксиран mock зависи от контекста. Строгият mock гарантира, че тестът не използва методи, чието поведение не е дефинирано — това повишава надеждността. Релаксираният mock е удобен за бързо прототипиране на тестове, където не всички зависимости са важни. На практика се препоръчва да се започне със строг mock и да се премине към relaxed само когато stubbing ът заема повече редове от самия тест.

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

Every блокът — е централната конструкция на stubbing в MockK. Внутри ламбдата се описва повикане на метод с конкретни аргументи, след което стойността се връща чрез returns, изключението се хвърля чрез throws или отговорът се изчислява чрез answers.

Различни начини на stubbing

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() }, който работи коректно с корутините и не изисква специален runner.

Проверка на броя повикания

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 за създаване на mockове чрез инициализация в JUnit 5. Разширението MockKExtension автоматично създава mockове преди всяк тест и изчиства след това — аналогично на MockitoExtension, но с подкрепа за relaxed режим.

Пример с MockKExtension

Анотацията @InjectMockKs (или алтернативата @MockK с изрично създаване на обект) инжектира mockовете в тествания инстанс. Това намалява 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 и обикновен mock в MockK?

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

Как да mockваме extension функции в MockK?

MockK поддържа mockване на extension функции чрез mockkStatic(). Това е възможно, защото extension функциите в Kotlin са статични методи с първи параметър-приемник. За всяка extension функция трябва да се посочи класът, в който е декларирана.

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

Да, MockK поддържа Kotlin Multiplatform (KMP) за общ код. На платформите JVM, Native и JS може да се използва общият API: mockk(), every, verify. Това прави MockK единственият избор за KMP проекти, където Mockito не работи.

Как да проверя реда на повиканията в MockK?

Използвайте verifySequence { } — блок, в който повиканията са посочени строго в очаквания ред. Ако действителният ред се различава, verifySequence ще хвърли изключение с посочване на първото несъвпадащо повикане.

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

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

Заключение

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

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

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също