Mock — co to je, mock-objekty a knihovny pro testy

Autor: IT Sectr Publikováno: 2026-04-10 Doba čtení: 8 min

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 — objekt ověřující interakce: které metody byly volány, s jakými argumenty a kolikrát
  • Mockito — nejpopulárnější knihovna pro vytváření Mock v Java a Android projektech
  • MockK — alternativa k Mockito pro Kotlin s nativní podporou korutin a sealed class
  • Behavior verification — klíčový rozdíl Mock od Stub: Mock ověřuje chování, nikoli stav
  • Over-mocking — hlavní Anti-Pattern: mock by měl být pouze pro externí závislosti

Co je Mock?

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í.

Jak Mock funguje

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.

Kdy je Mock nezbytný

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.

Mock a Stub: podrobné srovnání

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ériumMockStub
Hlavní otázkaByla metoda zavolána?Jaký výsledek byl vrácen?
OvěřeníChování (verify)Stavu (assert)
Vracení datVolitelnéPovinné
Příkladverify(analytics).logEvent("click")assertEquals(5, repository.getCount())
Kdy použítVedlejší účinkyVracení dat

Praktické pravidlo: Mock nebo ne

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.

Mockito a MockK: srovnání knihoven

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: osvědčená klasika

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: přístup Kotlin-first

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.

kotlin
// Mockito + mockito-kotlin
val repository = mock<UserRepository>()
whenever(repository.getUser(1)).thenReturn(user)

// MockK
val repository = mockk<UserRepository>()
every { repository.getUser(1) } returns user

Srovnání výkonu

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.

Příklady Mock testů v Kotlin

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.

Příklad 1: ViewModel s mockovanou analytikou

kotlin
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") }
    }
}

Příklad 2: UseCase s asynchronním ověřením

kotlin
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)
    }
}

Příklad 3: ověření argumentů s ArgumentCaptor

kotlin
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])
    }
}

Nejlepší postupy Mock testování

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í.

Mockujte pouze vnější hranice aplikace

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ů.

Jeden assert/verify na test

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).

  • Používejte relaxUnitFun = true v MockK pro metody vracející Unit — jinak Mock vyhodí výjimku na nepopsané volání
  • Omezte verify pouze na kritická volání — neověřujte každý getter a setter, to činí testy křehkými
  • Aplikujte ArgumentMatchers smysluplně — any() skrývá důležité detaily, pokud je argument kritický pro obchodní logiku
  • Nezneužívejte verifyNoMoreInteractions — tato metoda činí test zbytečně rigidním vůči jakýmkoli změnám v produkčním kódu
  • Používejte @MockkAnnotations pro automatickou inicializaci Mock objektů — to snižuje boilerplate a zlepšuje čitelnost

Pokročilé techniky Mock testování

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ů.

Částečný Mock s spyK

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.

Testování StateFlow s Turbine

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.

kotlin
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

Čím se liší Mock od Mockito?

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).

Jak Mock pracuje s Kotlin korutinami?

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).

Může Mock vracet různé hodnoty při opakovaných voláních?

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.

Jak vyčistit stav Mock mezi testy?

V MockK použijte anotaci @MockK s polem relaxed = true a zavolejte clearMocks(mock) v metodě @After. V MockitoMockito.reset(mock). Nejlepší praxí je vytvářet nový Mock pro každý test pomocí @Before, aby se vyloučil vliv mezi testy.

Jak Mock zpracovává sealed class v Kotlin?

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í

  • Mock — typ Test Double ověřující chování (verify), nikoli stav (assert) závislostí
  • Mockito — standard pro Java/Android, MockK — Kotlin-first volba s podporou korutin a sealed class
  • Hlavní pravidlo: Mock pro vnější hranice (síť, DB, systémové služby), reálné objekty pro vnitřní třídy
  • Over-mocking — hlavní Anti-Pattern: nadměrné nahrazování závislostí činí testy křehkými a málo užitečnými
  • Jeden test — jedna logická kontrola: verify pro Mock nebo assert pro Stub, ale ne obojí v jednom testu
  • ArgumentCaptor / slot — správný způsob ověření argumentů Mock volání namísto slepého any()
  • MockK je doporučen pro Kotlin projekty: coEvery a coVerify nativně pracují s korutinami bez dalších adaptérů

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í.

Prodiskutovat projekt

Přečtěte si také