Mock — wat is het, mock-objecten en bibliotheken voor tests

Auteur: IT Sectr Gepubliceerd: 2026-04-10 Leestijd: 8 min

Mock — is een vervangend object dat het gedrag van een echte component nabootst en het mogelijk maakt de interactie ermee te verifiëren. In tegenstelling tot Stub, dat simpelweg een bepaalde waarde teruggeeft, registreert Mock het feit dat een methode is aangeroepen, de doorgegeven argumenten en het aantal aanroepen. Volgens gegevens van Mockito (2024) is Mock het populairste type Test Double in Java en Kotlin-projecten, gebruikt in meer dan 70% van de unittesten van mobiele applicaties.

Belangrijkste

  • Mock — object dat interacties verifieert: welke methoden zijn aangeroepen, met welke argumenten en hoe vaak
  • Mockito — de populairste bibliotheek voor het maken van Mock in Java en Android-projecten
  • MockK — alternatief voor Mockito voor Kotlin met native ondersteuning voor coroutines en sealed class
  • Behavior verification — het belangrijkste verschil tussen Mock en Stub: Mock verifieert gedrag, niet toestand
  • Over-mocking — de belangrijkste Anti-Pattern: mock moet alleen voor externe afhankelijkheden zijn

Wat is Mock?

Mock — is een object dat wordt gemaakt door het mocking-framework (Mockito, MockK, EasyMock), dat een interface of klasse nabootst en alle aanroepen van zijn methoden registreert. De ontwikkelaar stelt verwachtingen in: methode X wordt aangeroepen met argumenten Y en retourneert Z. Na uitvoering van de test controleert Mock of de verwachtingen overeenkomen met de werkelijke aanroepen.

De term is afkomstig van de theatermetafoor van Test Doubles: Mock is een „navolger“ die niet alleen op het podium staat (zoals Dummy), maar een rol speelt en verifieert of de interactie ermee correct was. Als de geteste code de methode die Mock verwachtte niet heeft aangeroepen, of deze met verkeerde argumenten heeft aangeroepen — faalt de test met een bericht over een geschonden verwachting.

Hoe werkt Mock

Mock wordt gemaakt via de fabriek van het framework: mockk<MyInterface>() of Mockito.mock(MyClass.java). Het framework genereert een proxy-object dat alle methodeaanroepen onderschept. Elke aanroep wordt vergeleken met vooraf ingestelde verwachtingen (expectations). Als de aanroep overeenkomt met de verwachting — wordt de opgegeven waarde geretourneerd. Zo niet — retourneert Mock een standaardwaarde of gooit een uitzondering, afhankelijk van de configuratie.

Wanneer is Mock noodzakelijk

Mock is verplicht wanneer de geteste code interageert met componenten die bijwerkingen hebben: gegevens naar de server sturen, naar de database schrijven, loggen, analytics, navigatie, weergave van systeemdialoogvensters. Zonder Mock kunnen deze interacties niet worden geverifieerd zonder de echte infrastructuur op te starten. Volgens Google Testing Blog is Mock de enige manier om te controleren of de applicatie daadwerkelijk een analytics-gebeurtenis heeft verzonden zonder een testserver op te zetten.

Mock en Stub: gedetailleerde vergelijking

Het verschil tussen Mock en Stub is een van de meest besproken onderwerpen in testen. Beide typen vervangen de echte afhankelijkheid, maar op fundamenteel verschillende manieren.

CriteriumMockStub
Belangrijkste vraagIs de methode aangeroepen?Welk resultaat is teruggegeven?
VerificatieGedrag (verify)Toestand (assert)
Gegevens retournerenOptioneelVerplicht
Voorbeeldverify(analytics).logEvent("click")assertEquals(5, repository.getCount())
Wanneer gebruikenBijwerkingenGegevens retourneren

Praktische regel: Mock of niet

Een eenvoudige test om te kiezen: stel uzelf de vraag — „als ik deze coderegel verwijder, zal de test dan falen?“. Als de test de geretourneerde waarde verifieert — is Stub nodig (verificatie via assert). Als de test controleert of de code de methode met de juiste argumenten heeft aangeroepen — is Mock nodig (verificatie via verify). Deze tweedeling volgt uit het Command-Query Separation-patroon: methoden die de toestand wijzigen (commands) hebben Mock nodig; methoden die gegevens retourneren (queries) hebben Stub nodig.

Mockito en MockK: vergelijking van bibliotheken

De keuze tussen Mockito en MockK is een van de eerste beslissingen bij het inrichten van de teststack van een Android-project in Kotlin. Beide bibliotheken vervullen dezelfde taak, maar met een andere benadering van Kotlin-specifieke kenmerken.

Mockito: bewezen klassieker

Mockito — is de de facto standaard voor Java-projecten. Versie 5.x ondersteunt mock-objecten voor final-klassen, statische methoden en constructors dankzij de ingebouwde MockMaker. Voor Kotlin-projecten vereist Mockito aanvullende configuratie: mockito-kotlin-extensies voor verbeterde syntax, mockito-inline voor final-klassen. Mockito ondersteunt geen Kotlin-coroutines en suspend-functies zonder extra adapters.

MockK: Kotlin-first benadering

MockK is speciaal gemaakt voor Kotlin. Het ondersteunt native coroutines (coEvery, coVerify), sealed class, data class, object-singletons en extension-functies. De syntax van MockK gebruikt DSL met lambda-blokken, wat er natuurlijk uitziet in Kotlin-code. MockK kan ook eigenschappen (property mocking) mocken zonder extra configuratie — dit is belangrijk voor Android-projecten die LiveData, StateFlow en Delegates gebruiken.

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

Prestatievergelijking

Benchmarks (JVM Benchmark, 2024) tonen aan dat MockK mock-objecten 15–20% sneller maakt dan Mockito voor Kotlin-projecten dankzij directe verwerking van Kotlin-bytecode in plaats van Java Reflections. Voor projecten met duizenden unit-tests kan het verschil in bouwtijd merkbaar zijn: MockK bespaart 30–60 seconden bij het volledig doorlopen van tests in grote projecten.

Voorbeelden van Mock-tests in Kotlin

We bekijken drie scenario's: het testen van ViewModel met Mock-afhankelijkheden, het testen van UseCase met verificatie van API-aanroep en het testen van coroutines met coVerify.

Voorbeeld 1: ViewModel met gemockte analytics

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

Voorbeeld 2: UseCase met asynchrone verificatie

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

Voorbeeld 3: argumentverificatie met 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])
    }
}

Beste praktijken voor Mock-testen

Effectief gebruik van Mock in mobiele ontwikkeling vereist discipline. Overtreding van deze regels verandert tests in fragiele obstakels die bij elke refactoring breken.

Mock alleen externe grenzen van de applicatie

Strikte regel: Mock wordt alleen gemaakt voor afhankelijkheden die de grens van de applicatie overschrijden: API-cliënten, databases, bestandssysteem, systeemdiensten (LocationManager, BluetoothAdapter, Camera). Interne klassen van de applicatie — domeinentiteiten, Value Object, eenvoudige hulpprogramma's — mogen niet worden vervangen door Mock. Hun gedrag wordt getest via echte objecten.

Eén assert/verify per test

Elke test moet exact één logische controle bevatten — ofwel verify (voor Mock), ofwel assert (voor Stub). Meng verificatie van toestand en gedrag niet in één test. Als zowel de API-aanroep als het resultaat moeten worden gecontroleerd — maak dan twee afzonderlijke tests met verschillende namen. Deze regel, bekend als „één assert per test“, is afkomstig van de aanbevelingen van Kent Beck (2002).

  • Gebruik relaxUnitFun = true in MockK voor methoden die Unit retourneren — anders gooit Mock een uitzondering bij een niet-beschreven aanroep
  • Beperk verify tot alleen kritieke aanroepen — verifieer niet elke getter en setter, dit maakt tests fragiel
  • Pas ArgumentMatchers zinvol toe — any() verbergt belangrijke details als het argument cruciaal is voor de bedrijfslogica
  • Misbruik verifyNoMoreInteractions niet — deze methode maakt de test onnodig star voor elke verandering in productiecode
  • Gebruik @MockkAnnotations voor automatische initialisatie van Mock-objecten — dit vermindert boilerplate en verbetert de leesbaarheid

Geavanceerde technieken voor Mock-testen

Naast basaal mocken zijn er geavanceerde technieken die specifieke taken in mobiele ontwikkeling oplossen: het testen van multithreading, het verifiëren van Flow-toestand en het gedeeltelijk mocken van echte objecten.

Partial Mock met spyK

Spy (of partial mock) maakt het mogelijk een object te maken dat aanroepen naar de echte implementatie delegeert, maar het mogelijk maakt individuele methoden te overschrijven. In MockK wordt spyk gemaakt op basis van een echte instantie van de klasse: val repo = spyk(InMemoryUserRepository()). Aanroepen waarvoor verwachtingen zijn ingesteld via every gaan via Mock; de rest — via het echte object. Spy is vooral nuttig voor het testen van legacy-code waar dependency-injectie nog niet is geïmplementeerd en slechts één methode moet worden overschreven.

StateFlow testen met Turbine

In moderne Android-projecten met Jetpack Compose stelt ViewModel de toestand bloot via StateFlow. MockK maakt het mogelijk Flow-afhankelijkheden te mocken, en de Turbine-bibliotheek vereenvoudigt het verifiëren van emissie. Het klassieke patroon: MockK voor UseCase die Flow retourneert, Turbine voor het verifiëren van ViewModel-emissie. Deze stack wordt aanbevolen door de Android Testing-documentatie (Google, 2024) voor projecten op 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()
        }
    }
}

Veelgestelde vragen

Wat is het verschil tussen Mock en Mockito?

Mock — is een concept, een type Test Double dat gedrag verifieert. Mockito — is een bibliotheek voor het maken van Mock-objecten in Java en Android. Andere bibliotheken: MockK (Kotlin), EasyMock (Java), Cuckoo (iOS).

Hoe werkt Mock met Kotlin-coroutines?

Gebruik voor het testen van suspend-functies met Mock MockK (coEvery / coVerify) of Mockito met mockito-kotlin. MockK ondersteunt coroutines native: coEvery definieert het gedrag van de suspend-functie, coVerify verifieert de aanroep ervan binnen de coroutine. Alle suspend-aanroepen moeten worden uitgevoerd binnen runTest (kotlinx-coroutines-test).

Kan Mock verschillende waarden retourneren bij herhaalde aanroepen?

Ja. In MockK wordt hiervoor returnsMany gebruikt: every { api.getData() } returnsMany listOf(response1, response2). In Mockito — de keten thenReturn(value1).thenReturn(value2). Dit is nuttig voor het testen van gedrag bij opeenvolgende aanroepen met verschillende antwoorden.

Hoe kan ik de Mock-toestand tussen tests wissen?

In MockK gebruikt u de annotatie @MockK met het veld relaxed = true en roept u clearMocks(mock) aan in de @After-methode. In MockitoMockito.reset(mock). Beste praktijk: maak een nieuwe Mock voor elke test via @Before om invloed tussen tests uit te sluiten.

Hoe gaat Mock om met sealed class in Kotlin?

MockK werkt correct met sealed class: every { useCase() } returns Result.Success(data). Mockito ondersteunt sealed class niet direct en vereist omwegen. Dit is een van de redenen waarom voor Kotlin-projecten MockK wordt aanbevolen in plaats van Mockito.

Samenvatting

  • Mock — type Test Double dat gedrag (verify) verifieert, niet de toestand (assert) van afhankelijkheden
  • Mockito — standaard voor Java/Android, MockK — Kotlin-first keuze met ondersteuning voor coroutines en sealed class
  • Hoofdregel: Mock voor externe grenzen (netwerk, DB, systeemdiensten), echte objecten voor interne klassen
  • Over-mocking — de belangrijkste Anti-Pattern: overmatige vervanging van afhankelijkheden maakt tests fragiel en weinig nuttig
  • Eén test — één logische controle: verify voor Mock of assert voor Stub, maar niet beide in één test
  • ArgumentCaptor / slot — de juiste manier om argumenten van Mock-aanroep te verifiëren in plaats van blind any()
  • MockK wordt aanbevolen voor Kotlin-projecten: coEvery en coVerify werken native met coroutines zonder extra adapters

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook