Stub (Stub, Test-Double) — ein Testobjekt, das vordefinierte Antworten auf Methodenaufrufe anstelle einer echten Implementierung zurückgibt. In der mobilen Entwicklung isolieren Stubs das getestete Modul von Netzwerkanfragen, Datenbanken und Dateisystemen und ermöglichen so die Überprüfung der Logik ohne Umgebungseinrichtung. Im Gegensatz zu Mock überprüft Stub kein Verhalten — es liefert nur Daten. Mehr Details im Artikel von Martin Fowler über Test-Doubles.
Das Wichtigste
Stub ist ein Test-Double-Objekt, das eine echte Abhängigkeit in einem Test ersetzt und vordefinierte Werte für bestimmte Aufrufe zurückgibt. Der Begriff wurde in der Klassifizierung von Gerard Meszaros (2007) im Buch »xUnit Test Patterns« eingeführt. Stub gehört zur Kategorie der Test-Doubles — Objekte, die während des Tests echte Komponenten ersetzen. Das Hauptziel eines Stubs ist es, der getesteten Einheit vorhersagbare Daten zu liefern und die Unsicherheit externer Systeme zu beseitigen.
Funktionsweise — der Test konfiguriert den Stub vor der Ausführung: „wenn die Methode getUsers() aufgerufen wird, gib diese Benutzerliste zurück“. Stub enthält keine Geschïtslogik, überprüft nicht die Reihenfolge der Aufrufe und zeichnet keinen Aufrufverlauf auf. Es steht einfach an Stelle der echten Komponente und gibt zurück, was ihm gesagt wurde. Im Kontext von Android-Tests bedeutet dies: Der OkHttp-Client sendet keine echte Anfrage an den Server, sondern erhält eine Antwort von MockWebServer, das als Stub konfiguriert ist.
Wann verwenden — Stubs sind optimal für das Testen der UI-Ebene (ViewModel, Presenter) und der Geschäftslogik (UseCase, Interactor), wo die Reaktion auf bestimmte Daten überprüft werden muss: leere Liste, Server hat Fehler 500 zurückgegeben, Token abgelaufen. Jeder Fall, in dem der Test einen bestimmten Eingabezustand erfordert, ist eine Aufgabe für einen Stub. Jedes Testszenario erhält seine eigene Stub-Konfiguration, was die Tests lesbar und vorhersagbar macht.
Gerard Meszaros (2007) identifizierte im Buch »xUnit Test Patterns« fünf Arten von Test-Doubles: Dummy, Stub, Spy, Mock, Fake. Jeder Typ löst seine Aufgabe. Dummy — wird übergeben, aber nicht verwendet. Stub — gibt Daten zurück. Spy — zeichnet Aufrufe auf. Mock — überprüft Verhalten. Fake — enthält vereinfachte Logik. Das Verständnis dieser Klassifizierung hilft dem Entwickler, das richtige Werkzeug für jedes Testszenario auszuwählen.
Netzwerkanfragen — das häufigste Szenario für den Einsatz von Stubs. Die Anwendung tätigt HTTP-Aufrufe an die API, und der Test muss die Reaktion auf verschiedene Antworten überprüfen: erfolgreiches JSON, Fehler 401 (nicht autorisiert), Timeout, leeres Array. MockWebServer (OkHttp) auf Android und URLProtocol (iOS) fungieren als Stubs und geben vordefinierte HTTP-Antworten ohne echte Serververbindung zurück. Dies beschleunigt Tests von Sekunden auf Millisekunden.
Datenbank — Room (Android) und CoreData (iOS) haben In-Memory-Varianten, aber deren Einrichtung nimmt dennoch Zeit in Anspruch. Ein Stub anstelle des Repositorys gibt vorbereitete Entity-Listen zurück, ohne die Datenbank zu berühren. Dies ist besonders effektiv für das Testen von ViewModel, wo Sortierung, Filterung oder Datentransformation überprüft werden müssen. Der Test wird in Millisekunden ausgeführt, unabhängig vom Datenvolumen.
Systemdienste — LocationManager, SensorManager, SharedPreferences benötigen ein echtes Gerät oder einen Emulator. Stub für LocationProvider gibt vordefinierte Koordinaten zurück, für SensorManager — feste Beschleunigungssensorwerte. Auf iOS ist das Äquivalent CLLocationManager mit einer Test-Delegatenimplementierung. Ohne Stubs erfordern solche Tests ein physisches Gerät mit bestimmten Bedingungen.
Dateisystem und Cache — Bildladen, Antwort-Caching, Arbeiten mit Konfigurationsdateien — all diese Operationen hängen vom Festplattenzustand ab. Stub für FileManager oder ImageCache gibt Erfolg/Fehler zurück, ohne echte Dateien zu lesen. Dies eliminiert falsche Testfehlschläge aufgrund von Pfadabweichungen oder Zugriffsrechten auf verschiedenen Entwicklermaschinen.
Verantwortungsteilung — drei Arten von Test-Doubles lösen unterschiedliche Aufgaben. Stub: „gib mir Daten“. Mock: „überprüfe, dass ich aufgerufen wurde“. Fake: „ich funktioniere wie das Original, nur einfacher“. Der Unterschied ist entscheidend für die Lesbarkeit von Tests: Wenn ein Test Mock verwendet, wo Stub benötigt wird, ist er mit verify-Aufrufen überladen, die nichts mit dem getesteten Szenario zu tun haben.
| Merkmal | Stub | Mock | Fake |
|---|---|---|---|
| Zweck | Daten bereitstellen | Interaktion prüfen | Vereinfachte Implementierung |
| Logik | Nein | Nein | Ja (aber vereinfacht) |
| Verifikation | Nein | Ja (verify) | Indirekt (über Zustand) |
| Flexibilität | Niedrig — feste Antworten | Mittel | Hoch — Logik passt sich an |
| Geschwindigkeit | Maximal | Hoch | Mittel |
| Beispiel | MockWebServer gibt JSON zurück | Mockito.verify(repository).save() | InMemoryRepository mit HashMap |
Praktische Regel — wenn der Test prüft, welche Daten die getestete Komponente erhalten hat — verwenden Sie Stub. Wenn der Test prüft, ob die Komponente eine Abhängigkeitsmethode mit den richtigen Argumenten aufgerufen hat — verwenden Sie Mock. Wenn Sie einfach eine Datenbank durch eine Hash-Tabelle ersetzen möchten — das ist Fake. Das Mischen von Typen in einem Test macht ihn zerbrechlich: Bei einer Änderung der Implementierung müssen Sie sowohl die Stub- als auch die verify-Logik umschreiben.
Stub mit verify — ein häufiger Fehler, bei dem ein Entwickler einen Stub einrichtet und dann verify(stub).method() hinzufügt. Stub sollte per Definition nicht verifiziert werden — dafür gibt es Mock. Wenn Sie überprüfen müssen, ob eine Methode mit bestimmten Argumenten aufgerufen wurde, verwenden Sie Mockito.mock() anstelle von Mockito.stub(). Diese Trennung hält die Testabsicht für andere Entwickler klar.
MockWebServer — eine OkHttp-Bibliothek zum Erstellen von HTTP-Stubs auf Android und JVM. Sie startet einen lokalen HTTP-Server auf einem angegebenen Port, der OkHttp-Client-Anfragen abfängt und vordefinierte Antworten zurückgibt. Die Einrichtung dauert drei Zeilen: Server erstellen, Antwort enqueuen, starten. Der Test kann nacheinander mehrere Antworten für Szenarien mit Paginierung oder Wiederholungsversuchen enqueuen.
class UserRepositoryTest {
private val server = MockWebServer()
fun setup() {
server.start(8080)
val client = OkHttpClient.Builder()
.readTimeout(1, TimeUnit.SECONDS)
.build()
}
fun test_user_list_success() {
val json = "[{\"id\":1,\"name\":\"Alice\"}]"
server.enqueue(MockResponse()
.setBody(json)
.setResponseCode(200)
)
val result = repository.getUsers()
assertEquals(1, result.size)
}
fun teardown() {
server.shutdown()
}
}
MockK — eine Alternative zu Mockito für Kotlin mit erstklassiger Unterstützung für Coroutinen, Erweiterungsfunktionen und versiegelte Klassen. Stubs in MockK werden über coEvery (für suspend-Funktionen) und every (für reguläre Funktionen) erstellt. Im Gegensatz zu MockWebServer stubt MockK einzelne Abhängigkeitsmethoden anstelle der gesamten HTTP-Ebene. Dies ist praktisch für Unit-Tests von UseCase oder Interactor, bei denen Abhängigkeiten Repository-Abstraktionen sind.
interface UserRepository {
suspend fun getUsers(): List<User>
}
class GetUsersUseCaseTest {
private val repo = mockk<UserRepository>()
private val useCase = GetUsersUseCase(repo)
fun test_empty_list() = runTest {
coEvery { repo.getUsers() } returns emptyList()
val result = useCase.invoke()
assertTrue(result.isEmpty())
coVerify(exactly = 1) { repo.getUsers() }
}
}
Best Practice — für Integrationstests verwenden Sie MockWebServer (fängt echten HTTP ab), für Unit-Tests verwenden Sie MockK (stubt Schnittstellen). Stuben Sie nicht, was Sie nicht testen: Wenn der Test das Repository überprüft, stuben Sie nicht den OkHttp-Client darin — verwenden Sie einen echten MockWebServer auf HTTP-Ebene. Diese Regel hält die Tests relevant und reduziert die Zerbrechlichkeit bei Refactoring.
Swift-Protokolle als Stubs — im iOS-nativen Ansatz wird ein Stub durch das Einsetzen einer Teststruktur implementiert, die dem Abhängigkeitsprotokoll entspricht. Anstelle eines echten NetworkService erhält der Test einen StubNetworkService, der feste Daten zurückgibt. Swift ist eine statisch typisierte Sprache, daher muss der Stub demselben Protokoll entsprechen wie der echte Dienst. Der Compiler garantiert, dass der Stub alle erforderlichen Methoden implementiert.
protocol NetworkServiceProtocol {
func fetchUsers() async throws -> [User]
}
struct StubNetworkService: NetworkServiceProtocol {
let result: Result<[User], Error>
func fetchUsers() async throws -> [User] {
try result.get()
}
}
final class UsersViewModelTests: XCTestCase {
func test_success_state() async {
let stub = StubNetworkService(
result: .success([User(name: "Alice")])
)
let vm = UsersViewModel(service: stub)
await vm.load()
XCTAssertEqual(vm.users.count, 1)
}
}
OCMock für Objective-C — eine Bibliothek zum Erstellen von Stubs und Mocks in Legacy-iOS-Projekten. OCMock unterstützt Stub-Methoden mit Argumenten und Rückgabewerten. Moderne Swift-Projekte bevorzugen einen protokollbasierten Ansatz mit manuellen Stubs — dies gibt Kontrolle über jede Methode und erfordert keine externen Abhängigkeiten. OCMock bleibt eine Option für Projekte, bei denen die Protokollierung aller Abhängigkeiten wirtschaftlich nicht machbar ist.
URLProtocol für HTTP-Stubs — ein Systemmechanismus in iOS zum Abfangen von Netzwerkanfragen über eine URLProtocol-Unterklasse. Der Test registriert ein benutzerdefiniertes URLProtocol, das URLSession abfängt und Stub-Antworten zurückgibt. Vorteil gegenüber manuellen Stubs: Sie müssen die Anwendungsarchitektur nicht ändern — URLSession bleibt echt, aber die Daten werden auf Protokollebene ersetzt. Nachteil: Schwieriger zu debuggen als ein expliziter Stub-Dienst.
Häufig gestellte Fragen
Stub gibt vordefinierte Daten zurück und prüft nicht, ob ein Aufruf erfolgte. Mock überprüft zusätzlich, dass die Methode mit den richtigen Argumenten aufgerufen wurde (verify). Stub beantwortet die Frage „was zurückgeben“, Mock beantwortet „wurde der Aufruf getätigt“. Verwenden Sie Stub für Zustandsprüfung, Mock für Interaktionsprüfung.
Fake wird benötigt, wenn der Test eine funktionierende (wenn auch vereinfachte) Implementierung benötigt — zum Beispiel eine In-Memory-Datenbank anstelle von Room. Stub eignet sich für einzelne Szenarien mit vordefinierten Daten. Wenn Sie denselben Stub in 10 Tests wiederholen — benötigen Sie wahrscheinlich einen Fake. Fake reduziert Duplikation, weil die Logik in einer einzigen Klasse lebt.
Auf Android — MockK für Kotlin-Objekte (object) unterstützt mockkObject(), einschließlich statischer Methoden von Java-Klassen über mockkStatic(). Auf iOS — statische Swift-Methoden können nicht direkt gestubt werden; verwenden Sie Protokolle und DI, um einen static-Aufruf durch eine Instanzmethode eines Protokolls zu ersetzen. Statische Stubs sind technische Schulden und sollten in neuem Code vermieden werden.
Verwenden Sie MockWebServer (OkHttp) — es funktioniert als lokaler HTTP-Server, der Antworten enqueued. Für Retrofit ersetzen Sie einfach die Basis-URL durch localhost:8080. Für Ktor verwenden Sie MockEngine — einen integrierten Mechanismus zum Ersetzen von HttpStatement. Beide Ansätze funktionieren ohne echtes Internet und geben volle Kontrolle über Statuscode, Body und Antwort-Header.
Spy umschließt ein reales Objekt und zeichnet Aufrufe auf, während Stub das Objekt vollständig durch feste Antworten ersetzt. Spy erlaubt die teilweise Nutzung der echten Implementierung (andere Methoden funktionieren wie sie sind), während Stub dies nicht tut. Wenn Sie prüfen müssen, ob eine Methode aufgerufen wurde, aber ein Teil der Logik ausgeführt werden soll — verwenden Sie Spy, nicht Stub.
Zusammenfassung
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.
Lesen Sie auch