Glossar · Testgrundlagen
Test Double
Englisch: Test double
Auch genannt: Test-Double, Testdouble, Testattrappe, Mock vs Stub
Definition
Jedes Objekt, das in einem Test eine echte Abhängigkeit ersetzt, etwa Dummy, Stub, Fake, Spy oder Mock, damit der getestete Code schnell und vorhersagbar läuft.
Ein Test Double ersetzt eine echte Abhängigkeit, etwa ein Payment-Gateway, eine Datenbank, eine Uhr oder einen E-Mail-Dienst, durch etwas, das der Test kontrolliert. Der von Gerard Meszaros bekannt gemachte Name stammt vom Stuntdouble im Film. Doubles machen Tests schnell und deterministisch und erlauben es, Situationen zu simulieren, die in der Realität schwer herzustellen sind, etwa einen Timeout oder eine abgelehnte Karte.
Die fünf Arten
| Typ | Was er tut | Beispiel |
|---|---|---|
| Dummy | Wird nur übergeben, um einen Parameter zu füllen, und nie verwendet | Ein null oder leerer Logger, den die Methode verlangt, den dieser Test aber nie anfasst |
| Stub | Liefert vorgefertigte Antworten auf Aufrufe | rates.getRate("EUR") liefert immer 0.92 |
| Spy | Ein Stub, der zusätzlich aufzeichnet, wie er aufgerufen wurde | Zeichnet auf, dass send() einmal mit einer bestimmten Adresse aufgerufen wurde |
| Mock | Mit Erwartungen vorprogrammiert; der Test verifiziert die Interaktionen | Prüfen, dass gateway.charge(4999) genau einmal aufgerufen wurde |
| Fake | Eine funktionierende, aber vereinfachte Implementierung | Ein In-Memory-Repository auf Basis einer HashMap |
Umgangssprachlich wird „Mock“ für all diese Arten verwendet, und Bibliotheken wie Mockito erzeugen Objekte, die als Stubs, Spies oder Mocks agieren können. Die Unterscheidung zu kennen hilft trotzdem, besonders bei Stub vs. Mock: Ein Stub liefert dem getesteten Code Eingaben (Zustandsverifikation), während ein Mock die Ausgaben prüft, die der Code an seine Abhängigkeiten sendet (Verhaltensverifikation).
Beispiel (JUnit 5 mit Mockito)
@Test
void declinedCardDoesNotSendConfirmation() {
PaymentGateway gateway = mock(PaymentGateway.class);
EmailService email = mock(EmailService.class);
when(gateway.charge(4999)).thenReturn(PaymentResult.DECLINED); // stub
var checkout = new Checkout(gateway, email);
var result = checkout.placeOrder(4999);
assertEquals(OrderStatus.PAYMENT_FAILED, result.status());
verify(email, never()).sendConfirmation(any()); // mock-style verification
}
(Statische Imports aus org.mockito.Mockito, org.mockito.ArgumentMatchers und org.junit.jupiter.api.Assertions sind weggelassen.)
Häufige Fehler
- Alles mocken. Tests, die jeden internen Aufruf verifizieren, brechen bei jedem Refactoring und können bestehen, während die echte Integration kaputt ist. Mocken Sie an Architekturgrenzen; bevorzugen Sie innerhalb davon echte Objekte oder Fakes.
- Doubles, die sich von der Realität entfernen. Eine gestubbte API, die ein Feld liefert, das der echte Dienst vor Monaten umbenannt hat, erzeugt falsche Sicherheit. Contract-Tests und einige echte Integrationstests halten Doubles ehrlich.
- Typen mocken, die Ihnen nicht gehören. Kapseln Sie Clients von Drittanbietern in Ihr eigenes Interface und ersetzen Sie stattdessen dieses durch ein Double.