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 — 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.
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.
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.
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.
| Criterium | Mock | Stub |
|---|---|---|
| Belangrijkste vraag | Is de methode aangeroepen? | Welk resultaat is teruggegeven? |
| Verificatie | Gedrag (verify) | Toestand (assert) |
| Gegevens retourneren | Optioneel | Verplicht |
| Voorbeeld | verify(analytics).logEvent("click") | assertEquals(5, repository.getCount()) |
| Wanneer gebruiken | Bijwerkingen | Gegevens retourneren |
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.
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 — 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 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.
// Mockito + mockito-kotlin
val repository = mock<UserRepository>()
whenever(repository.getUser(1)).thenReturn(user)
// MockK
val repository = mockk<UserRepository>()
every { repository.getUser(1) } returns user
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.
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.
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])
}
}
Effectief gebruik van Mock in mobiele ontwikkeling vereist discipline. Overtreding van deze regels verandert tests in fragiele obstakels die bij elke refactoring breken.
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.
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).
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.
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.
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.
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
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).
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).
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.
In MockK gebruikt u de annotatie @MockK met het veld relaxed = true en roept u clearMocks(mock) aan in de @After-methode. In Mockito — Mockito.reset(mock). Beste praktijk: maak een nieuwe Mock voor elke test via @Before om invloed tussen tests uit te sluiten.
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
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.
Lees ook