Mockito: Partielle Mocks

Aus MimiPedia
Version vom 9. März 2023, 18:40 Uhr von Ullrich (Diskussion | Beiträge) (Ullrich verschob die Seite Partielle Mocks nach Mockito: Partielle Mocks)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)

Mock vs. Spy

Neben dem hier beschriebenen Mocks giebt es ein weiteres Konzept in Mockito, das dem Mocki sehr ähnlich ist: der Spy. Bei einem Spy handelt es sich um ein Objekt, das um ein anderes Objekt herumgewickelt wird. Gewöhnlich wird es verwendet um Laufzeit-Informationen über das Objekt zu sammeln. Man kann damit etwa überprüfen, ob eine Methode des Objekts aufgerufen wurde oder nicht -- sehr praktisch in Unit-Tests. Ein Spy wird so erzeugt:

Foo foo = new Foo();
Foo spiedFoo = Mockito.spy(foo);

Im Gegensatz zum Mock benötigt der Spy für die Erzeugung ein Objekt. Das entstehende Spy-Objekt hat den gleichen Typ wie das umwickelte Objekt. Wie der Mock entsteht dabei eine Unterklasse. Einen Mock braucht man nicht mit einem Spy zu umwickeln, er hat bereits die gleiche Funktionalität die der Spy erzeugt. Im Folgenden geht es um die unterschiedliche Verwendung von Mock und Spy, Für die Untersuchung des Aufrufverhaltens giebt es eine eigene Seite.

Zwei extreme Mocks

Die Klasse, die hier mit der mock-Methode erzeugt wird

Echt mock = Mockito.mock(Echt.class);

ersetzt alle Methoden der Klasse Echt durch gemockte Methoden. Man kann also getrost von einem totalen Mock sprechen. Ein Objekt der Klasse Echt ist ein gewissermaßen ein echtes Objekt und hingegen erwartungsgemäß keine einzige Methode der Klasse. Man könnte in ihr einen entartetetn Mock sehen und als Null-Mock bezeichnen.

Objekte der originalen Klasse können nicht unmittelbar mit Mockito manipuliert werden, man umwickelt sie daher mit einem Mockito-Proxy-Objekt, einem Spy:

Echt spy = Mockito.spy(new Echt());

Zwischen diesen beiden Extremen, dem "Alles" des totalen Mock und dem "Nichts" des Null-Mock liegt ein weites Feld von Möglichkeiten um Teile der Original-Klasse durch mock-Methoden zu ersetzt und Teile davon unberührt zu lassen: das sind die partiellen Mocks.

Um einen partiellen Mock zu bauen gibt es zwei diametrale Vorgehensweisen: Entweder man beginnt beim totalen Mock und läßt einzelne Methoden die Funktionalität der Original-Klasse ausführen; oder man beginnt beim Null-Mock und mockt einzelne Methoden, deren originale Funktionalität nicht ausgeführt werden soll. Für welches Vorgehen man sich entscheidet hängt vom Anwendungsfall ab.

Den totalen Mock aufweichen

die Klasse Echt sei so definiert:

class Echt {
    int eins() {
        return 1;
    }
    int zwei(int x) {
        return x + 3;
    }
}

Der eingangs erzeugte Mock mockt beide Methoden. Die Methode eins soll nun nicht mehr gemockt werden, das geht so:

Echt mock = Mockito.mock(Echt.class);
Mockito.when(mock.eins()).thenCallRealMethod();

Mehr ist dazu eigentlich nicht zu sagen. Der Aufruf von eins() liefert nun wie erwartet den Wert 1, während der Aufruf von zwei() für jedes Argument den Wert 0 liefert. Man hat nun für jede Methode drei Möglichkeiten die man nach belieben anwenden kann:

  • Nichts tun und die gemockte Methode behalten
  • Die echte Methode der Superklasse aufrufen
  • Die Methode mit speziellem Mock-Verhalten ausstatten

Die Mock-Anweisungen lassen sich beliebig mischen, wie dieses Beispiel mit Methode zwei der Klasse Echt zeigt:

Echt mock = Mockito.mock(Echt.class);
Mockito.when(mock.drei(Mockito.anyInt())).thenCallRealMethod();
Mockito.when(mock.drei(Mockito.eq(5))).thenReturn(4711);

Bei Aufruf von mock.zwei(5) erhalten wir den Wert 4711 aus dem Mock und in allen anderen Fällen das Ergebnis der echten Methode. Auch hier ist zu beachten, daß allgemeinere Matcher vor spezielleren Matchern kommen. In diesem Falle also .anyInt() vor .eq(5).

Den Null-Mock aushöhlen

Wir beginnen nun mit dem Spy, der als Null-Mock das komplette Original-Verhalten der Klasse Echt erhält. Diesmal möchten wir die Methode eins mocken, während die Methode zwe) weiterhin das reale Verhalten aufweist:

Echt spy= Mockito.spy(new Echt());
Mockito.when(spy.eins()).thenReturn(3);

Und fertig ist die Laube.