Mockito: Partielle Mocks
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.