MockK — je Kotlin-first framework pro vytváření mock objektů, navržený speciálně pro ekosystém Kotlin s ohledem na jeho jazykové vlastnosti: korutiny, extension funkce, data class a sealed class. Na rozdíl od Mockita, který byl portován na Kotlin z Javy, MockK byl od počátku navržen pro Kotlin syntaxi a nevyžaduje další pluginy pro práci s final třídami. Podle údajů MockK.io je knihovna používána ve více než 40% Kotlin projektů s modulárním testováním.
Hlavní body
MockK — je knihovna pro vytváření mock objektů, napsaná v Kotlinu a optimalizovaná pro jeho syntaxi. Řeší stejné úkoly jako Mockito — izolaci testovaného kódu od závislostí — ale dělá to pomocí konstrukcí specifických pro Kotlin: lambd, DSL, reified generics a suspend funkcí.
Hlavní výhoda MockKu oproti portovaným řešením — nativní podpora Kotlinu. V Mockitu vyžaduje mockování final třídy opt-in (mockito-inline) a statických metod mockStatic. MockK to podporuje standardně, protože Kotlin třídy jsou standardně final a obcházení tohoto omezení je zabudováno do architektury knihovny.
Verze 1.13.12 (2024) — stabilní vydání, které podporuje Kotlin 2.0, K2 kompilátor a multi-platformní projekty (KMP). MockK také funguje s Kotlin/Native a Kotlin/JS, což z něj dělá jedinou volbu pro KMP projekty, kde nejsou použitelné ani Mockito, ani EasyMock.
MockK je navržen s ohledem na specifika Kotlinu a využívá jazykové schopnosti — reified generics, DSL s lambdami, inline funkce — k poskytnutí stručného a typově bezpečného API bez ztráty výkonu.
Mechanismus MockKu je založen na bytecode instrumentaci pomocí knihovny ByteBuddy (stejně jako Mockito), ale obaluje ji do Kotlin-přátelského DSL. Místo řetězů when().thenReturn() MockK používá lambda bloky every { } a coEvery { }, které vypadají jako přirozené rozšíření jazyka. Pod kapotou MockK zachycuje volání uvnitř lambdy, analyzuje metodu a argumenty pomocí reflexe a porovnává je s zaznamenanými pravidly stubbingu.
Blok every { mock.method() } returns value se čte jako „pokaždé, když je metoda volána, vrať hodnotu“. Taková deklarativní syntaxe je blíže stylu Kotlinu a odstraňuje zmatek s pořadím argumentů v when(). Díky reified generics v Kotlinu je typ mocku odvozen automaticky bez explicitního určení třídy.
val repository = mockk<UserRepository>()
// Stubbing: každé volání findById(1) vrátí uživatele
every { repository.findById(1) } returns User("Alice")
// Volání a ověření
val result = repository.findById(1)
assertEquals("Alice", result.name)
Na rozdíl od Mockita, kde každou metodu je třeba explicitně nakonfigurovat, MockK podporuje relaxed mock — mock, který vrátí „rozumné“ výchozí hodnoty pro jakoukoli metodu: prázdný seznam pro List, 0 pro Int, prázdný řetězec pro String. To dramaticky snížuje množství přípravného kódu.
// Relaxed mock — všechny metody vracejí výchozí hodnoty
val api = mockk<ApiService>(relaxed = true)
// Nevyžaduje stubbing — vrátí prázdný seznam
println(api.getUsers()) // []
MockK nabízí několik způsobů vytváření mock objektů: mockk<T>() pro přísný mock (každá metoda musí být explicitně nakonfigurována), mockk<T>(relaxed = true) pro relaxovaný mock a spyk(obj) pro vytvoření spy na reálném objektu.
| Funkce | Typ | Chování bez stubbingu |
|---|---|---|
| mockk() | Přísný mock | Hází výjimku při volání nestubované metody |
| mockk(relaxed = true) | Relaxovaný mock | Vrací výchozí hodnotu |
| spyk() | Spy | Volá reálnou metodu, pokud není nakonfigurován stub |
| slot() | Argument Captor | Zachycuje argument pro ověření |
Výběr mezi přísným a relaxovaným mockem závisí na kontextu. Přísný mock zaručuje, že test nepoužívá metody, jejichž chování není definováno — to zvyšuje spolehlivost. Relaxovaný mock je vhodný pro rychlé prototypování testů, kde nejsou všechny závislosti důležité. V praxi se doporučuje začít s přísným mockem a přejít na relaxed pouze tehdy, když stubbing zabírá více řádků než samotný test.
Blok every — centrální konstrukce stubbingu v MockKu. Uvnitř lambdy je popsáno volání metody s konkrétními argumenty a poté je hodnota vrácena pomocí returns, výjimka je hozena pomocí throws nebo je odpověď vypočítána pomocí answers.
MockK podporuje všechny scénáře potřebné pro testování: vracení hodnoty, házení výjimky, výpočet odpovědi na základě argumentů, několik odpovědí po sobě (sekvence volání).
// Vracení hodnoty
every { repo.findById(1) } returns User("Alice")
// Házení výjimky
every { repo.findById(999) } throws NotFoundException()
// Dynamická odpověď
every { repo.save(any()) } answers {
val user = firstArg<User>()
user.copy(id = 42)
}
// Sekvence odpovědí
every { repo.findAll() } returnsMany listOf(
listOf(User("Alice")),
listOf(User("Bob")),
emptyList()
)
Verify v MockKu je významem analogický Mockito.verify(), ale používá Kotlin DSL: verify { mock.method() }. Pro suspend funkce se používá coVerify { mock.suspendMethod() }, který správně funguje s korutinami a nevyžaduje speciální runner.
MockK podporuje stejné modifikátory jako Mockito: exactly(1), atLeast(2), atMost(5), wasNot(Called). Syntaxe je minimalistická — modifikátor je předán jako první argument v verify { }.
// Ověření: metoda zavolána přesně 1x
verify(exactly = 1) { repo.save(any()) }
// Kontrola pořadí volání
verifySequence {
repo.save(any())
repo.flush()
}
// coVerify pro suspend funkce
coVerify { api.fetchUsers() }
Pro ověření argumentů se používá slot() — analog ArgumentCaptoru. Slot je deklarován před voláním, předán do every nebo verify a po provedení testu obsahuje zachycenou hodnotu.
val userSlot = slot<User>()
verify { repo.save(capture(userSlot)) }
assertEquals("Alice", userSlot.captured.name)
MockK poskytuje anotace @MockK a @RelaxedMockK pro vytváření mocků pomocí inicializace v JUnit 5. Rozšíření MockKExtension automaticky vytváří mocky před každým testem a čistí po něm — analogicky k MockitoExtension, ale s podporou relaxed režimu.
Anotace @InjectMockKs (nebo alternativa @MockK s explicitním vytvořením objektu) vkládá mocky do testované instance. To snižuje boilerplate a činí kód testu čistějším.
@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)
}
}
Výběr mezi MockK a Mockitem závisí na složení týmu a typu projektu. Mockito má větší ekosystém, více příkladů a integrací, ale MockK poskytuje čistější Kotlin syntaxi a nativní podporu jazykových funkcí. Pro nové Kotlin projekty je MockK doporučen jako idiomatickější řešení.
| Kritérium | MockK | Mockito |
|---|---|---|
| Syntaxe | Kotlin DSL (every, verify) | Java styl (when, thenReturn) |
| Korutiny | coEvery, coVerify (nativně) | Vyžaduje další knihovny |
| Final class | Podporováno standardně | Vyžaduje mockito-inline |
| KMP | Podporováno | Není podporováno |
| Relaxed mock | Vestavěný | Nemá analog |
| Popularita | Rostoucí v Kotlin komunitě | Dominuje v Java a hybridních projektech |
Pro projekty v čistém Kotlinu (bez Java tříd) je MockK preferovanější: méně boilerplate, nativní podpora korutin, žádná překvapení s final třídami. Pro hybridní projekty nebo týmy s Java pozadím zůstává Mockito fungující variantou — obě knihovny lze použít v jednom projektu prostřednictvím různých modulů. Při migraci z Mockita na MockK stačí nahradit anotace @Mock za @MockK a přepsat bloky when().thenReturn() do formátu every { }.
Často kladené dotazy
Relaxed mock vrací výchozí hodnoty pro všechny nestubované metody (prázdný seznam, 0, null), aniž by házel výjimky. Běžný (strict) mock vyžaduje explicitní stubbing každé metody — jinak test selže. Relaxed mock je vhodný pro rychlé testy, strict — pro spolehlivé.
MockK podporuje mockování extension funkcí pomocí mockkStatic(). To je možné, protože extension funkce v Kotlinu jsou statické metody s prvním parametrem-příjemcem. Pro každou extension funkci je třeba určit třídu, ve které je deklarována.
Ano, MockK podporuje Kotlin Multiplatform (KMP) pro společný kód. Na platformách JVM, Native a JS lze použít společné API mockk(), every, verify. To dělá z MockKu jedinou volbu pro KMP projekty, kde Mockito nefunguje.
Použijte verifySequence { } — blok, ve kterém jsou volání uvedena přísně v očekávaném pořadí. Pokud se skutečné pořadí liší, verifySequence hodí výjimku s označením prvního neshodného volání.
Ano, technicky je to možné, ale nedoporučuje se. Konflikty mohou vzniknout na úrovni bytecode instrumentace (ByteBuddy vs mockito-inline). Pokud projekt již používá Mockito, migrace na MockK může být postupná prostřednictvím izolace modulů.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také