Mock — was ist das, Mock-Objekte und Bibliotheken für Tests

Autor: IT Sectr Veröffentlicht: 2026-04-10 Lesezeit: 8 Min.

Mock ist ein Ersatzobjekt, das das Verhalten einer realen Komponente nachahmt und die Überprüfung von Interaktionen mit ihr ermöglicht. Im Gegensatz zu Stub, das einfach einen vorgegebenen Wert zurückgibt, zeichnet Mock den Aufruf der Methode, die übergebenen Argumente und die Anzahl der Aufrufe auf. Laut Mockito (2024) ist Mock der beliebteste Test-Double-Typ in Java- und Kotlin-Projekten, der in über 70 % der Modultests mobiler Anwendungen verwendet wird.

Das Wichtigste

  • Mock — ein Objekt, das Interaktionen überprüft: welche Methoden aufgerufen wurden, mit welchen Argumenten und wie oft
  • Mockito — die beliebteste Bibliothek zur Erstellung von Mocks in Java- und Android-Projekten
  • MockK — eine Alternative zu Mockito für Kotlin mit nativer Unterstützung für Coroutinen und versiegelte Klassen
  • Behavior verification — der Hauptunterschied zwischen Mock und Stub: Mock prüft Verhalten, nicht Zustand
  • Over-mocking — das wichtigste Antimuster: Mocks sollten nur für externe Abhängigkeiten verwendet werden

Was ist Mock?

Mock ist ein Objekt, das von einem Mocking-Framework (Mockito, MockK, EasyMock) erstellt wird, eine Schnittstelle oder Klasse simuliert und alle Aufrufe seiner Methoden aufzeichnet. Der Entwickler legt Erwartungen fest: Methode X wird mit den Argumenten Y aufgerufen und gibt Z zurück. Nach der Testausführung überprüft Mock, ob die Erwartungen mit den tatsächlichen Aufrufen übereinstimmen.

Der Begriff stammt aus der theatralischen Metapher der Test Doubles: Ein Mock ist ein „Imitator“, der nicht nur auf der Bühne steht (wie ein Dummy), sondern eine Rolle spielt und überprüft, ob die Interaktion mit ihm korrekt war. Wenn der getestete Code die Methode, die Mock erwartete, nicht aufgerufen hat oder mit falschen Argumenten aufgerufen hat — schlägt der Test mit einer Meldung über eine verletzte Erwartung fehl.

Wie Mock funktioniert

Ein Mock wird über die Factory des Frameworks erstellt: mockk<MyInterface>() oder Mockito.mock(MyClass.java). Das Framework generiert ein Proxy-Objekt, das alle Methodenaufrufe abfängt. Jeder Aufruf wird mit vordefinierten Erwartungen verglichen. Wenn der Aufruf einer Erwartung entspricht — wird der angegebene Wert zurückgegeben. Wenn nicht — gibt Mock je nach Konfiguration einen Standardwert zurück oder wirft eine Ausnahme.

Wann Mock notwendig ist

Mock ist unverzichtbar, wenn der getestete Code mit Komponenten interagiert, die Nebenwirkungen haben: Senden von Daten an den Server, Schreiben in die Datenbank, Protokollierung, Analytics, Navigation, Anzeigen von Systemdialogen. Ohne Mocks können diese Interaktionen nicht überprüft werden, ohne eine reale Infrastruktur zu betreiben. Laut Google Testing Blog ist Mock die einzige Möglichkeit zu überprüfen, ob eine Anwendung tatsächlich ein Analytics-Ereignis gesendet hat, ohne einen Testserver zu starten.

Mock vs. Stub: detaillierter Vergleich

Der Unterschied zwischen Mock und Stub ist eines der am meisten diskutierten Themen beim Testen. Beide Typen ersetzen eine reale Abhängigkeit, aber auf grundlegend unterschiedliche Weise.

KriteriumMockStub
HauptfrageWurde die Methode aufgerufen?Welches Ergebnis wurde zurückgegeben?
VerifikationVerhalten (verify)Zustand (assert)
DatenrückgabeOptionalErforderlich
Beispielverify(analytics).logEvent(„click“)assertEquals(5, repository.getCount())
Wann verwendenNebenwirkungenDatenrückgabe

Praktische Regel: Mock oder nicht

Ein einfacher Test zur Entscheidung: Fragen Sie sich „Wenn ich diese Codezeile lösche, wird der Test dann fehlschlagen?“ Wenn der Test einen Rückgabewert prüft — benötigen Sie einen Stub (assert-basierte Verifikation). Wenn der Test prüft, ob der Code eine Methode mit den richtigen Argumenten aufgerufen hat — benötigen Sie einen Mock (verify-basierte Verifikation). Diese Dichotomie folgt dem Command-Query-Separation-Muster: Methoden, die den Zustand ändern (Commands), benötigen Mocks; Methoden, die Daten zurückgeben (Queries), benötigen Stubs.

Mockito vs. MockK: Bibliotheksvergleich

Die Wahl zwischen Mockito und MockK ist eine der ersten Entscheidungen bei der Einrichtung des Test-Stacks für ein Android-Projekt in Kotlin. Beide Bibliotheken erfüllen den gleichen Zweck, jedoch mit unterschiedlichen Ansätzen für Kotlin-spezifische Funktionen.

Mockito: Bewährte Klassik

Mockito ist der De-facto-Standard für Java-Projekte. Version 5.x unterstützt dank des integrierten MockMaker das Mocken von finalen Klassen, statischen Methoden und Konstruktoren. Für Kotlin-Projekte benötigt Mockito zusätzliche Einrichtung: mockito-kotlin-Erweiterungen für verbesserte Syntax, mockito-inline für finale Klassen. Mockito unterstützt keine Kotlin-Coroutinen und Suspend-Funktionen ohne zusätzliche Adapter.

MockK: Kotlin-First-Ansatz

MockK wurde speziell für Kotlin entwickelt. Es unterstützt nativ Coroutinen (coEvery, coVerify), versiegelte Klassen, Datenklassen, Objekt-Singletons und Erweiterungsfunktionen. Die MockK-Syntax verwendet DSL mit Lambda-Blöcken, was in Kotlin-Code natürlich wirkt. MockK unterstützt auch Property-Mocking ohne zusätzliche Einrichtung — wichtig für Android-Projekte, die LiveData, StateFlow und Delegates verwenden.

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

Leistungsvergleich

Benchmarks (JVM Benchmark, 2024) zeigen, dass MockK Mock-Objekte 15–20 % schneller erstellt als Mockito für Kotlin-Projekte, dank direkter Arbeit mit Kotlin-Bytecode statt Java Reflections. Bei Projekten mit Tausenden von Modultests kann der Unterschied in der Build-Geschwindigkeit spürbar sein: MockK spart 30–60 Sekunden bei einem vollständigen Testdurchlauf in großen Projekten.

Mock-Testbeispiele in Kotlin

Betrachten wir drei Szenarien: Testen eines ViewModels mit Mock-Abhängigkeiten, Testen eines UseCase mit API-Aufrufüberprüfung und Testen von Coroutinen mit coVerify.

Beispiel 1: ViewModel mit Mock-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") }
    }
}

Beispiel 2: UseCase mit asynchroner Verifikation

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

Beispiel 3: Argumentüberprüfung mit 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 Praktiken für Mock-Tests

Die effektive Nutzung von Mock in der mobilen Entwicklung erfordert Disziplin. Die Verletzung dieser Regeln verwandelt Tests in fragile Hindernisse, die bei jeder Refaktorisierung brechen.

Nur externe Anwendungsgrenzen mocken

Eine strenge Regel: Mocks sollten nur für Abhängigkeiten erstellt werden, die die Anwendungsgrenze überschreiten: API-Clients, Datenbanken, Dateisystem, Systemdienste (LocationManager, BluetoothAdapter, Camera). Interne Anwendungsklassen — Domain-Entitäten, Value Objects, einfache Hilfsprogramme — sollten nicht durch Mocks ersetzt werden. Ihr Verhalten wird durch reale Objekte getestet.

Ein Assert / Verify pro Test

Jeder Test sollte genau eine logische Prüfung enthalten — entweder verify (für Mock) oder assert (für Stub). Mischen Sie nicht Zustands- und Verhaltensüberprüfung in einem Test. Wenn Sie sowohl einen API-Aufruf als auch sein Ergebnis überprüfen müssen — erstellen Sie zwei separate Tests mit unterschiedlichen Namen. Diese Regel, bekannt als „ein Assert pro Test“, geht auf die Empfehlungen von Kent Beck (2002) zurück.

  • Verwenden Sie relaxUnitFun = true in MockK für Methoden, die Unit zurückgeben — sonst wirft Mock eine Ausnahme bei einem nicht spezifizierten Aufruf
  • Beschränken Sie verify auf kritische Aufrufe — überprüfen Sie nicht jeden Getter und Setter, das macht Tests fragil
  • Wenden Sie ArgumentMatchers sinnvoll an — any() verbirgt wichtige Details, wenn das Argument für die Geschäftslogik kritisch ist
  • Verwenden Sie verifyNoMoreInteractions nicht übermäßig — diese Methode macht Tests unnötig starr gegenüber Änderungen im Produktionscode
  • Verwenden Sie @MockkAnnotations zur automatischen Initialisierung von Mock-Objekten — das reduziert Boilerplate und verbessert die Lesbarkeit

Fortgeschrittene Mock-Testtechniken

Über das grundlegende Mocking hinaus gibt es fortgeschrittene Techniken, die spezifische Aufgaben in der mobilen Entwicklung lösen: Testen von Multithreading, Überprüfen des Flow-Zustands und partielles Mocken realer Objekte.

Partielles Mock mit spyK

Spy (oder partielles Mock) ermöglicht es, ein Objekt zu erstellen, das Aufrufe an die reale Implementierung delegiert, aber einzelne Methoden überschreiben lässt. In MockK wird spyk basierend auf einer realen Klasseninstanz erstellt: val repo = spyk(InMemoryUserRepository()). Aufrufe mit über every definierten Erwartungen gehen durch den Mock; der Rest durch das reale Objekt. Spy ist besonders nützlich zum Testen von Legacy-Code, bei dem die Abhängigkeitsinjektion noch nicht implementiert ist und Sie nur eine Methode überschreiben müssen.

Testen von StateFlow mit Turbine

In modernen Android-Projekten mit Jetpack Compose gibt der ViewModel den Zustand über StateFlow bekannt. MockK ermöglicht das Mocken von Flow-Abhängigkeiten, und die Turbine-Bibliothek vereinfacht die Emissionsüberprüfung. Das klassische Muster: MockK für einen UseCase, der einen Flow zurückgibt, Turbine zur Überprüfung der ViewModel-Emissionen. Dieser Stack wird von der Android-Testing-Dokumentation (Google, 2024) für Projekte mit Kotlin Coroutines empfohlen.

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

Häufig gestellte Fragen

Wie unterscheidet sich Mock von Mockito?

Mock ist ein Konzept, eine Art Test Double, das Verhalten überprüft. Mockito ist eine Bibliothek zum Erstellen von Mock-Objekten in Java und Android. Andere Bibliotheken: MockK (Kotlin), EasyMock (Java), Cuckoo (iOS).

Wie funktioniert Mock mit Kotlin-Coroutinen?

Zum Testen von Suspend-Funktionen mit Mock verwenden Sie MockK (coEvery / coVerify) oder Mockito mit mockito-kotlin. MockK unterstützt Coroutinen nativ: coEvery definiert das Verhalten einer Suspend-Funktion, coVerify überprüft ihren Aufruf innerhalb einer Coroutine. Alle Suspend-Aufrufe müssen innerhalb von runTest (kotlinx-coroutines-test) ausgeführt werden.

Kann ein Mock bei wiederholten Aufrufen unterschiedliche Werte zurückgeben?

Ja. In MockK verwenden Sie returnsMany: every { api.getData() } returnsMany listOf(response1, response2). In Mockito — eine Kette von thenReturn(value1).thenReturn(value2). Dies ist nützlich zum Testen von Verhalten bei sequenziellen Aufrufen mit unterschiedlichen Antworten.

Wie löscht man den Mock-Status zwischen Tests?

In MockK verwenden Sie die @MockK-Annotation mit relaxed = true und rufen clearMocks(mock) in der @After-Methode auf. In MockitoMockito.reset(mock). Beste Praxis: Erstellen Sie für jeden Test über @Before einen neuen Mock, um Interferenzen zwischen Tests zu vermeiden.

Wie behandelt Mock versiegelte Klassen in Kotlin?

MockK funktioniert korrekt mit versiegelten Klassen: every { useCase() } returns Result.Success(data). Mockito unterstützt versiegelte Klassen nicht direkt und erfordert Workarounds. Dies ist ein Grund, warum MockK für Kotlin-Projekte gegenüber Mockito empfohlen wird.

Zusammenfassung

  • Mock — ein Test-Double-Typ, der das Verhalten (verify) und nicht den Zustand (assert) von Abhängigkeiten überprüft
  • Mockito — der Standard für Java/Android, MockK — die Kotlin-First-Wahl mit Unterstützung für Coroutinen und versiegelte Klassen
  • Hauptregel: Mock für externe Grenzen (Netzwerk, DB, Systemdienste), reale Objekte für interne Klassen
  • Over-mocking — das Haupt-Antimuster: übermäßiges Ersetzen von Abhängigkeiten macht Tests fragil und wenig nützlich
  • Ein Test — eine logische Prüfung: verify für Mock oder assert für Stub, aber nicht beides im selben Test
  • ArgumentCaptor / slot — der richtige Weg, um Mock-Aufrufargumente anstelle von blindem any() zu überprüfen
  • MockK wird für Kotlin-Projekte empfohlen: coEvery und coVerify arbeiten nativ mit Coroutinen ohne zusätzliche Adapter

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch