Mock — je náhradní objekt, který napodobuje chování reálné komponenty a umožňuje ověřovat interakci s ní. Na rozdíl od Stub, který jednoduše vrací zadanou hodnotu, Mock zaznamenává fakt volání metody, předané argumenty a počet volání. Podle údajů Mockito (2024) je Mock nejpopulárnějším typem Test Double v Java a Kotlin projektech, používaný ve více než 70% unit-testů mobilních aplikací.
Hlavní body
Mock — je objekt vytvořený mocking frameworkem (Mockito, MockK, EasyMock), který napodobuje rozhraní nebo třídu a zaznamenává všechna volání svých metod. Vývojář nastavuje očekávání: metoda X bude volána s argumenty Y a vrátí Z. Po provedení testu Mock zkontroluje, zda se očekávání shodují se skutečnými voláními.
Termín pochází z divadelní metafory Test Doubles: Mock je „napodobitel“, který nejen stojí na scéně (jako Dummy), ale hraje roli a ověřuje, zda s ním byla interakce správná. Pokud testovaný kód nevolal metodu, kterou Mock očekával, nebo ji volal s nesprávnými argumenty — test selže se zprávou o porušeném očekávání.
Mock je vytvořen pomocí továrny frameworku: mockk<MyInterface>() nebo Mockito.mock(MyClass.java). Framework generuje proxy objekt, který zachycuje všechna volání metod. Každé volání je porovnáno s předem nastavenými očekáváními (expectations). Pokud volání odpovídá očekávání — je vrácena zadaná hodnota. Pokud ne — Mock vrátí výchozí hodnotu nebo vyhodí výjimku, v závislosti na konfiguraci.
Mock je povinný, když testovaný kód interaguje s komponentami, které mají vedlejší účinky: odesílání dat na server, zápis do databáze, logování, analytika, navigace, zobrazení systémových dialogů. Bez Mock nelze tyto interakce ověřit bez spuštění skutečné infrastruktury. Podle Google Testing Blog je Mock jediným způsobem, jak zkontrolovat, že aplikace skutečně odeslala analytickou událost bez zvednutí testovacího serveru.
Rozdíl mezi Mock a Stub je jedním z nejdiskutovanějších témat v testování. Oba typy nahrazují skutečnou závislost, ale zásadně odlišnými způsoby.
| Kritérium | Mock | Stub |
|---|---|---|
| Hlavní otázka | Byla metoda zavolána? | Jaký výsledek byl vrácen? |
| Ověření | Chování (verify) | Stavu (assert) |
| Vracení dat | Volitelné | Povinné |
| Příklad | verify(analytics).logEvent("click") | assertEquals(5, repository.getCount()) |
| Kdy použít | Vedlejší účinky | Vracení dat |
Jednoduchý test pro výběr: položte si otázku — „když smažu tento řádek kódu, selže test?“. Pokud test ověřuje vrácenou hodnotu — je potřeba Stub (ověření pomocí assert). Pokud test zjišťuje, zda kód zavolal metodu se správnými argumenty — je potřeba Mock (ověření pomocí verify). Tato dichotomie vyplývá z vzoru Command-Query Separation: metody měnící stav (commands) vyžadují Mock; metody vracející data (queries) vyžadují Stub.
Volba mezi Mockito a MockK je jedním z prvních rozhodnutí při nastavování testovacího stacku Android projektu v Kotlin. Obě knihovny plní stejný úkol, ale s odlišným přístupem ke specifikům Kotlin.
Mockito — je de facto standard pro Java projekty. Verze 5.x podporuje mock objekty pro finální třídy, statické metody a konstruktory díky vestavěnému MockMakeru. Pro Kotlin projekty vyžaduje Mockito dodatečnou konfiguraci: rozšíření mockito-kotlin pro lepší syntaxi, mockito-inline pro finální třídy. Mockito nepodporuje Kotlin korutiny a suspend funkce bez dalších adaptérů.
MockK byl vytvořen speciálně pro Kotlin. Nativně podporuje korutiny (coEvery, coVerify), sealed class, data class, object-singletony a extension funkce. Syntaxe MockK používá DSL s lambda bloky, což vypadá přirozeně v Kotlin kódu. MockK také umí mockovat vlastnosti (property mocking) bez dodatečné konfigurace — to je důležité pro Android projekty používající LiveData, StateFlow a Delegates.
// Mockito + mockito-kotlin
val repository = mock<UserRepository>()
whenever(repository.getUser(1)).thenReturn(user)
// MockK
val repository = mockk<UserRepository>()
every { repository.getUser(1) } returns user
Benchmarky (JVM Benchmark, 2024) ukazují, že MockK vytváří mock objekty o 15–20% rychleji než Mockito pro Kotlin projekty díky přímé práci s bytecode Kotlin, nikoli Java Reflections. U projektů s tisíci unit-testů může být rozdíl v čase sestavení znatelný: MockK šetří 30–60 sekund při plném průchodu testů ve velkých projektech.
Prozkoumáme tři scénáře: testování ViewModel s Mock závislostmi, testování UseCase s ověřením API volání a testování korutin s coVerify.
class ProfileViewModelTest {
private val analytics = mockk<AnalyticsService>()
private val repo = mockk<UserRepository>()
private val vm = ProfileViewModel(repo, analytics)
fun `profile opened logs analytics event`() {
every { analytics.logEvent("profile_opened") } returns Unit
vm.onViewCreated()
verify { analytics.logEvent("profile_opened") }
}
}
class SendMessageUseCaseTest {
private val api = mockk<MessagingApi>()
private val useCase = SendMessageUseCase(api)
fun `send message with correct payload`() = runTest {
val message = Message(text = "Hello", userId = 42)
coEvery { api.sendMessage(any()) } returns MessageResult.Sent("msg_1")
val result = useCase.execute(message)
coVerify {
api.sendMessage(match {
it.text == "Hello" && it.userId == 42
})
}
assertTrue(result is MessageResult.Sent)
}
}
class OrderUseCaseTest {
private val api = mockk<OrderApi>()
private val useCase = OrderUseCase(api)
private val slot = slot<OrderRequest>()
fun `order request contains correct items`() = runTest {
coEvery { api.placeOrder(capture(slot)) } returns OrderResult.Placed("order_1")
useCase.execute(listOf("item_a", "item_b"))
assertEquals(2, slot.captured.items.size)
assertEquals("item_a", slot.captured.items[0])
}
}
Efektivní použití Mock v mobilním vývoji vyžaduje disciplínu. Porušení těchto pravidel mění testy na křehké překážky, které se při každém refaktorování rozbijí.
Přísné pravidlo: Mock je vytvářen pouze pro závislosti překračující hranici aplikace: API klienty, databáze, souborový systém, systémové služby (LocationManager, BluetoothAdapter, Camera). Vnitřní třídy aplikace — doménové entity, Value Object, jednoduché nástroje — by neměly být nahrazovány Mockem. Jejich chování se testuje pomocí reálných objektů.
Každý test by měl obsahovat přesně jednu logickou kontrolu — buď verify (pro Mock), nebo assert (pro Stub). Nemíchejte ověření stavu a chování v jednom testu. Pokud je třeba zkontrolovat jak API volání, tak výsledek — vytvořte dva samostatné testy s různými názvy. Toto pravidlo, známé jako „jeden assert na test“, vychází z doporučení Kenta Becka (2002).
Kromě základního mockování existují pokročilé techniky, které řeší specifické úkoly v mobilním vývoji: testování vícevláknovosti, ověřování stavu Flow a částečné mockování reálných objektů.
Spy (nebo částečný mock) umožňuje vytvořit objekt, který deleguje volání na skutečnou implementaci, ale umožňuje přepsání jednotlivých metod. V MockK se spyk vytváří na základě skutečné instance třídy: val repo = spyk(InMemoryUserRepository()). Volání, pro která jsou nastavena očekávání prostřednictvím every, jdou přes Mock; ostatní — přes skutečný objekt. Spy je užitečný zejména pro testování legacy kódu, kde ještě není zavedena injekce závislostí, a je třeba přepsat pouze jednu metodu.
V moderních Android projektech na Jetpack Compose ViewModel vystavuje stav prostřednictvím StateFlow. MockK umožňuje mockování Flow závislostí a knihovna Turbine zjednodušuje ověřování emise. Klasický vzor: MockK pro UseCase vracející Flow, Turbine pro ověření emise ViewModel. Tento stack je doporučen dokumentací Android Testing (Google, 2024) pro projekty na Kotlin Coroutines.
class SearchViewModelTest {
private val searchUseCase = mockk<SearchUseCase>()
private val vm = SearchViewModel(searchUseCase)
fun `search emits results`() = runTest {
coEvery { searchUseCase.search("android") } returns
flowOf(SearchResult.Success(listOf(Item("Android TDD"))))
vm.search("android")
vm.state.test {
val state = awaitItem()
assertTrue(state.items.isNotEmpty())
cancelAndIgnoreRemainingEvents()
}
}
}
Často kladené otázky
Mock — je koncept, typ Test Double ověřující chování. Mockito — je knihovna pro vytváření Mock objektů v Java a Android. Další knihovny: MockK (Kotlin), EasyMock (Java), Cuckoo (iOS).
Pro testování suspend funkcí s Mock použijte MockK (coEvery / coVerify) nebo Mockito s mockito-kotlin. MockK podporuje korutiny nativně: coEvery definuje chování suspend funkce, coVerify ověřuje její volání uvnitř korutiny. Všechna suspend volání musí být provedena uvnitř runTest (kotlinx-coroutines-test).
Ano. V MockK se k tomu používá returnsMany: every { api.getData() } returnsMany listOf(response1, response2). V Mockito — řetězec thenReturn(value1).thenReturn(value2). To je užitečné pro testování chování při sekvenčních voláních s různými odpovědmi.
V MockK použijte anotaci @MockK s polem relaxed = true a zavolejte clearMocks(mock) v metodě @After. V Mockito — Mockito.reset(mock). Nejlepší praxí je vytvářet nový Mock pro každý test pomocí @Before, aby se vyloučil vliv mezi testy.
MockK správně pracuje se sealed class: every { useCase() } returns Result.Success(data). Mockito nepodporuje sealed class přímo a vyžaduje obejití. To je jeden z důvodů, proč se pro Kotlin projekty doporučuje MockK místo Mockito.
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é