Über Artikel
Hier ist ein weiterer Leitfaden für Mockito. Darin habe ich einerseits versucht, die Funktionalität dieser Bibliothek so zu beschreiben, dass ein mit ihr nicht vertrauter Leser sofort die Möglichkeit bekam, sie vollständig zu nutzen, und nicht nur eine allgemeine Vorstellung davon. Andererseits wollte ich es kompakt und strukturiert genug machen, damit ich es schnell in seiner Gesamtheit lesen und schnell etwas darin finden konnte, was einmal gelesen, aber vergessen wurde. Im Allgemeinen dieser Artikel, der für mich selbst nützlich wäre, als ich gerade auf diese Bibliothek stieß und nicht wirklich verstand, wie es funktioniert.
Ich nehme an, es kann mir jetzt nützlich sein - manchmal vergesse ich etwas davon, und es ist am bequemsten, das Material nicht gemäß der offiziellen Dokumentation oder den Artikeln anderer Leute abzurufen, sondern gemäß meiner eigenen, sagen wir, Zusammenfassung. Gleichzeitig habe ich versucht, den Text so zu gestalten, dass er vor allem dazu geeignet ist, Mockito von Grund auf neu kennenzulernen, und an einigen Stellen analysiere ich scheinbar offensichtliche Dinge im Detail - nicht alle waren mir von Anfang an klar.
Inhalt:
- Mockito: Was ist das und warum ist es notwendig?
- Umwelt, Versionen und Versuchstier
- verspotten und spionieren
- Verhaltensmanagement
- Anrufbedingungen einstellen
- Anrufergebnisse einstellen
- Tracking Method Calls
- Mock-Objekte als Feldwerte und Mockito-Annotationen
- Rollback-Verhalten auf Standard- und Mockito-Sitzungen
- Was sonst?
Mockito: Was ist das und warum ist es notwendig?
Kurz gesagt, Mockito ist ein Stub-Framework.
Wie Sie wissen, muss das zu testende Element beim Testen von Code (hauptsächlich Unit-Tests, aber nicht nur) häufig Instanzen von Klassen bereitstellen, die es beim Arbeiten verwenden sollte. Oft müssen sie jedoch nicht voll funktionsfähig sein - im Gegenteil, sie müssen sich streng definiert verhalten, damit ihr Verhalten einfach und vollständig vorhersehbar ist. Sie werden Stubs genannt. Um sie zu erhalten, können Sie alternative Testimplementierungen von Schnittstellen erstellen, die erforderlichen Klassen mit einer Neudefinition der Funktionalität erben usw. Dies alles ist jedoch recht unpraktisch, redundant und mit Fehlern behaftet. Eine in jeder Hinsicht bequemere Lösung sind spezielle Frameworks zum Erstellen von Stubs. Einer davon (und vielleicht der berühmteste für Java) ist Mockito.
Mit Mockito können Sie mit einer einzigen Codezeile den sogenannten Mock (so etwas wie die Basis für den gewünschten Stub) jeder Klasse erstellen. Für einen solchen Mock ist unmittelbar nach der Erstellung ein bestimmtes Standardverhalten charakteristisch (alle Methoden geben zuvor bekannte Werte zurück - normalerweise ist dies null
oder 0
). Sie können dieses Verhalten nach Ihren Wünschen neu definieren, es mit dem richtigen Detaillierungsgrad steuern und so weiter. Infolgedessen wird Mock zu einem Stub mit den erforderlichen Eigenschaften. Im Folgenden werde ich ausführlich erläutern, wie dies zu tun ist.
Ich stelle fest, dass Mock auch für diese Klassen erstellt werden kann, deren neue Instanz Sie nicht nur Klassen mit ausschließlich privaten Konstruktoren wie Singleton- und Utility-Klassen und mit einer minimalen Konfiguration des Frameworks und der Aufzählungen erstellen können.
Umwelt, Versionen und Versuchstier
Beim Schreiben dieses Artikels habe ich verwendet:
- Mockito: 'org.mockito: mockito-core: 2.24.0' (neueste stabile Version zum Zeitpunkt des Schreibens)
- TestNG: 'org.testng: testng: 6.14.3' als Testframework
- AssertJ: 'org.assertj: assertj-core: 3.11.1' als Validierungswerkzeug
- Lombok: 'org.projectlombok: lombok: 1.18.6' (nur zur Vereinfachung)
- Java 8
Für meine unmenschlichen Experimente habe ich diese Schnittstelle eines Dienstes geschrieben, der den Zugriff auf bestimmte Daten ermöglicht.
public interface DataService { void saveData(List<String> dataToSave); String getDataById(String id); String getDataById(String id, Supplier<String> calculateIfAbsent); List<String> getData(); List<String> getDataListByIds(List<String> idList); List<String> getDataByRequest(DataSearchRequest request); }
Und dieser (der Ordnung halber) Code der Anforderungsklasse, der an die letzte der Schnittstellenmethoden übergeben wurde.
@AllArgsConstructor @Getter class DataSearchRequest { String id; Date updatedBefore; int length; }
Dateneinheiten werden anhand der ID identifiziert und weisen einige weitere Merkmale auf. Sie sind jedoch direkt in der Form, in der sie vom Dienst zurückgegeben werden, Zeichenfolgen und keine komplexeren Objekte. Ich vermisse nichts Wichtiges und die Beispiele sind einfacher und klarer.
Ich werde sofort bemerken: In den folgenden Beispielen rufe ich die überschriebenen Methoden meiner Scheinobjekte aus Gründen der Klarheit direkt auf, aber bei echten Tests ist die Idee überhaupt nicht! In diesem Test würde ich konsequent Folgendes tun:
- konfigurierte den Mock meines Dienstes nach Bedarf;
- hat es (höchstwahrscheinlich über den Konstruktor) an eine Instanz einer anderen Klasse übergeben, die es verwendet (angenommen, es enthält eine Art Geschäftslogik unter Verwendung der vom
DataService
bereitgestellten DataService
), die ich tatsächlich testen würde; - aktivierte die Funktionalität der getesteten Klasse und kontrollierte die Ergebnisse;
- Bei Bedarf würde ich die Anzahl und Reihenfolge der Aufrufe der Methode (n) meines Modells steuern, die von der getesteten Klasse als Ergebnis der vorherigen Aktion aufgerufen werden sollten.
verspotten und spionieren
Die zentrale Klasse von Mockito, über die auf die meisten Funktionen BDDMockito
soll, ist in der Tat eine Klasse namens Mockito
(es gibt auch die BDDMockito
Klasse, die ungefähr die gleichen Möglichkeiten in einer für BDD besser geeigneten Form bietet, aber hier werde ich nicht weiter darauf eingehen). . Der Zugriff auf die Funktionalität erfolgt über statische Methoden.
Um ein DataService
der DataService
Klasse zu erstellen, muss ich nur Folgendes tun:
DataService dataServiceMock = Mockito.mock(DataService.class);
Fertig - Ich habe eine Instanz der Klasse, die ich brauche. Es wird von jeder Methode oder jedem Konstruktor akzeptiert, für die ein Parameter dieses Typs erforderlich ist (z. B. der Konstruktor der Klasse, die ich testen möchte). Selbst wenn eine instanceof DataService
später darauf wartet, wird sie bestanden: Nicht nur die instanceof DataService
gibt true
, sondern auch dataServiceMock.getClass()
- nämlich DataService.class
. In gewisser formaler Weise stellt sich heraus, dass die programmatische Unterscheidung eines Scheinobjekts von einem gewöhnlichen eine ziemlich schwierige Aufgabe ist, was logisch ist: Schließlich soll die erste nur von der zweiten nicht zu unterscheiden sein. Mockito verfügt jedoch über ein Tool dafür - die Mockito.mockingDetails
Methode. Wenn ich ein beliebiges Objekt übergebe, erhalte ich ein Objekt der MockingDetails
Klasse. Es enthält Informationen darüber, was dieses Objekt aus Sicht von Mockito darstellt: ob es sich um eine Schein- oder Spionage handelt (siehe unten), wie es verwendet wurde, wie es erstellt wurde und so weiter.
Besonders hervorzuheben ist die Situation, in der ich versuche, ein Modell für die letzte Klasse oder eine Scheininstanz von enum zu erstellen oder das Verhalten der endgültigen Methode zu überschreiben. In diesem Fall weigert sich der obige Code mit dem Standardverhalten von Mockito, genau unter diesen Umständen zu funktionieren. Dies kann jedoch geändert werden. Erstellen Sie einfach im Projekt (mit dem Standardgerät des Projektverzeichnisbaums) die Datei test/resources/mockito-extensions/org.mockito.plugins.MockMaker
und geben Sie die folgende Zeile ein:
mock-maker-inline
Danach können Sie die endgültigen Klassen und Aufzählungen auf die übliche Weise nachahmen und die endgültigen Methoden überschreiben.
Das Modell, das ich in Aktion gesetzt habe, ist so merkwürdig wie möglich: Keine einzige Methode hat Auswirkungen auf irgendetwas, und der zurückgegebene Wert ist für Objekttypen null
und für primitive 0
. Bitte beachten Sie: Wenn die Methode eine Sammlung zurückgibt, gibt das Standardmodell keine null
, sondern leere Sammlungsinstanzen. Für List
sich beispielsweise heraus, dass dies eine leere LinkedList
unabhängig davon, was die reale Methode hätte zurückgeben sollen. Aber als Werte von Arrays, Grundelementen oder Objekten erhalte ich null
. Das Standardverhalten (und nicht nur das Verhalten) kann mithilfe der Funktionalität der MockSettings
Klasse geändert werden. Dies ist jedoch selten erforderlich.
Auf die eine oder andere Weise benötige ich in den meisten Fällen nicht das Standardverhalten, und im nächsten Abschnitt werde ich detailliert analysieren, wie stattdessen festgelegt wird, was stattdessen erforderlich ist.
Was ist jedoch, wenn ich ein reales Klassenobjekt mit der verfügbaren Funktionalität als Stub verwenden möchte, um die Funktionsweise nur eines Teils seiner Methoden neu zu definieren? Wenn es sich um Unit-Tests handelt, weist ein solcher Bedarf normalerweise (aber nicht immer) darauf hin, dass das Projekt mit dem Design nicht in Ordnung ist, und dies wird im Prinzip nicht empfohlen. Es gibt jedoch Situationen, in denen dies aus irgendeinem Grund nicht vermieden werden kann. In diesem Fall hat Mockito den sogenannten Spion "Spione". Im Gegensatz zu Mocks können sie sowohl basierend auf der Klasse als auch auf dem fertigen Objekt erstellt werden:
DataService dataServiceSpy = Mockito.spy(DataService.class);
Wenn beim Erstellen eines Spions basierend auf einer Klasse ein Typ eine Schnittstelle ist, wird ein reguläres Scheinobjekt erstellt. Wenn der Typ eine Klasse ist, versucht Mockito, eine Instanz mit dem Standardkonstruktor (ohne Parameter) zu erstellen. Und nur wenn es keinen solchen Konstruktor gibt, tritt ein Fehler auf und der Test funktioniert nicht.
Das Verhalten von Spionageobjekten ist standardmäßig identisch mit dem Verhalten einer regulären Klasseninstanz, bietet mir jedoch die gleichen Möglichkeiten wie Scheinobjekte: Sie ermöglichen mir, ihr Verhalten neu zu definieren und ihre Verwendung zu überwachen (siehe die folgenden Abschnitte). Ein wichtiger Punkt: Spion ist kein Wrapper um die Instanz, auf der er erstellt wurde! Daher hat der Aufruf der Spionagemethode keinen Einfluss auf den Status der ursprünglichen Instanz.
Verhaltensmanagement
Also, wie man sich lächerlich macht oder ausspioniert, um das zu tun, was ich brauche. Außerdem werde ich überall einfach "verspotten" schreiben - dies bedeutet "verspotten oder spionieren", sofern nicht ausdrücklich anders angegeben.
Im Allgemeinen beruht die Steuerung des Verhaltens eines Scheinobjekts auf einem offensichtlichen Konzept: Wenn ein Schein auf diese Weise beeinflusst wurde (dh eine solche und eine solche Methode wurde mit solchen und solchen Argumenten aufgerufen), sollte er auf diese und jene Weise reagieren. Dieses Konzept hat zwei Implementierungen innerhalb der Mockito-Klasse - die Hauptimplementierung, die von Entwicklern empfohlen wird, wo immer dies möglich ist, und die alternative, die verwendet wird, wenn die Hauptkonzeption nicht geeignet ist.
Die Hauptimplementierung basiert auf der Methode Mockito.when
. Diese Methode nimmt als "Parameter" einen Aufruf der neu definierten Methode des OngoingStubbing
(auf diese Weise wird die erkannte Aktion behoben) und gibt ein Objekt vom Typ OngoingStubbing
, mit dem eine der Methoden der Mockito.then...
-Familie Mockito.then...
(so wird die Reaktion auf diesen Effekt festgelegt). Alles in allem sieht es im einfachsten Fall ungefähr so aus:
List<String> data = new ArrayList<>(); data.add("dataItem"); Mockito.when(dataService.getAllData()).thenReturn(data);
Nach dieser Operation getAllData()
ich durch Aufrufen der Methode getAllData()
für das getAllData()
Objekt das in der ersten Zeile der Liste angegebene Objekt.
Hier kann die vertraute "objektorientierte" Intuition zu einer Fehlfunktion führen, weshalb es sich lohnt, näher darauf einzugehen. Aus Sicht der Java-Syntax ist der an die when
Methode als Parameter übergebene Wert natürlich der Wert, der von der überschriebenen Methode zurückgegeben wird. Für mock ist dies ein leerer Wert, für spy ist dies der Wert, der von der Methode des realen Objekts zurückgegeben wird. Dank der Magie, die „unter der Haube“ von Mockito wirkt, funktioniert die when
Methode jedoch nur dann normal (und stürzt beim Starten mit einem Fehler nicht ab), wenn sich der Aufruf der Mock-Object-Methode nach dem when
in den Klammern befindet.
Eine ähnliche Ideologie funktioniert häufig, wenn das Verhalten eines Mocks in einem Mockito definiert wird: Durch Aufrufen einer Methode (eines Mock-Objekts oder einer Klasse des Mockito
) versuche ich, den von ihm zurückgegebenen Wert nicht zu erhalten, sondern irgendwie den möglichen Aufruf der Methode des Mock-Objekts zu beeinflussen, mit dem ich arbeite: angeben seine Grenzen, das Ergebnis festlegen, die Beobachtung seiner Herausforderungen festlegen und so weiter. Ich gebe zu, es klingt ein bisschen neblig, und bei der ersten Kollision sieht es seltsam aus, aber nachdem Sie es herausgefunden haben, werden Sie diesen Ansatz im Zusammenhang mit der Arbeit mit Stubs bald als völlig natürlich empfinden.
Eine alternative Implementierung zum Verknüpfen der Bedingung und des Ergebnisses des Aufrufs sind die Methoden der Mockito.do...
-Familie. Mit diesen Methoden können Sie das Verhalten beginnend mit dem Ergebnis des Aufrufs festlegen und ein Objekt der Stubber
Klasse zurückgeben, mit dem Sie die Bedingung bereits festlegen können. Die gleiche Bindung wie oben auf diese Weise sieht folgendermaßen aus:
List<String> data = new ArrayList<>(); data.add("dataItem"); Mockito.doReturn(data).when(dataService).getData()
Was ist der Unterschied, warum das Binden über Mockito.when
als vorzuziehen angesehen wird und wann Sie noch die Methoden von Mockito.do...
? Bitte beachten Sie: In der ersten Implementierung wird beim Festlegen des Verhaltens der Methode (in diesem Fall getAllData()
) zuerst der Aufruf der noch nicht neu definierten Version ausgeführt, und erst dann tritt im Darm von Mockito eine Überschreibung auf. Im zweiten Fall tritt ein solcher Aufruf nicht auf - die Stubber.when
Methode Stubber.when
direkt an die Stubber.when
Methode übergeben, und ein Objekt desselben Typs, das von dieser Methode zurückgegeben wird, aber anderer Art ist, wird mit einer überschreibbaren Methode aufgerufen. Dieser Unterschied bestimmt alles. Die Bindung über Mockito.do...
steuert zur Kompilierungszeit nicht, welche neu definierbare Methode ich aufrufen werde und ob sie mit dem angegebenen Rückgabewert typkompatibel ist. Daher ist normalerweise Mockito.when
vorzuziehen - daran kann kein Fehler liegen. Es kann jedoch Fälle geben, in denen ich vermeiden möchte, eine überschriebene Methode aufzurufen. Für einen frisch erstellten Mock ist ein solcher Aufruf durchaus akzeptabel. Wenn ich diese Methode jedoch bereits neu definiert habe oder mich mit Spionage befasse, ist dies möglicherweise unerwünscht, und das Auslösen einer Ausnahme lässt die erforderliche Neudefinition überhaupt nicht zu . Und hier kommt die Bindung durch Mockito.do...
zur Mockito.do...
Eine andere Situation, in der Sie nicht auf die Methoden Mockito.do...
können, ist das Überschreiben der Methode, die void
Mockito.when
: Der ausstehende Parameter Mockito.when
kann mit einer solchen Methode nicht funktionieren. Mockito.doReturn
, aber es gibt Mockito.doThrow
, Mockito.doAnswer
und selten genug Mockito.doNothing
.
Als nächstes werde ich etwas detaillierter überlegen, wie die Bedingungen und Ergebnisse von Anrufen festgelegt werden. Ich werde nur in Betracht ziehen, über Mockito.when
binden. Mockito.when
ein alternativer Weg in der Handhabung fast völlig ähnlich ist.
Anrufbedingungen einstellen
Das obige Beispiel betrifft eine Methode ohne Parameter, und die zugehörige Aufrufbedingung ist eines möglich - die Tatsache des Aufrufs. Sobald Parameter erscheinen, wird die Situation komplizierter. Um eine Methode aufzurufen, deren Verhalten ich einstelle, muss ich mindestens etwas an sie übergeben. Aber noch etwas ist wichtiger: Es kann sich herausstellen, dass ich nicht immer die gegebene Reaktion erhalten möchte, sondern nur, wenn ich sie mit Parametern aufrufe, die bestimmte Anforderungen erfüllen. DataService
hat folgende Methode:
String getDataItemById(String id) {
Wenn ich unabhängig von den Argumenten eine Antwort auf einen Aufruf dieser Methode Mockito.any
muss, muss ich die Methode Mockito.any
verwenden:
Mockito.when(dataService.getDataItemById(any())) .thenReturn("dataItem");
Wenn ich Mock brauche, um nur auf einen bestimmten Wert des Arguments zu reagieren, können Sie diesen Wert direkt oder die Methoden von Mockito.eq
(wenn es sich um eine Äquivalenz handelt) oder Mockito.same
(wenn ein Mockito.same
erforderlich ist) verwenden:
Mockito.when(dataService.getDataItemById("idValue")) .thenReturn("dataItem");
Und wenn ich möchte, dass das Argument einige Anforderungen erfüllt, gibt es eine Reihe praktischer statischer Methoden derselben Mockito
Klasse (z. B. können Zeichenfolgen am Anfang oder am Ende einer bestimmten Zeichenfolge, beim Mustervergleich usw. auf Inhalt überprüft werden). Es gibt auch eine allgemeine Methode Mockito.argThat (und ihre Analoga für primitive Typen), die die Implementierung der ArgumentMatcher-Funktionsschnittstelle akzeptiert:
Mockito.when(dataService.getDataById( Mockito.argThat(arg -> arg == null || arg.length() > 5))) .thenReturn("dataItem");
Mit den Klassen ArgumentMatchers
und AdditionalMatchers
können Sie mit einigen nützlichen Standardimplementierungen dieser Schnittstelle arbeiten. Mit AdditionalMatchers.or
und AdditionalMatchers.and
können Sie beispielsweise andere Matcher kombinieren (Hinweis: Die statischen Methoden dieser Klassen geben keine Instanzen von Matchern zurück, sondern greifen nur darauf zu!).
Für dieselbe Methode können Sie das Verhalten mehrmals mit unterschiedlichen Anforderungen an die Argumente festlegen, und alle auf diese Weise definierten Verhaltensmodelle wirken gleichzeitig. In einigen Fällen können sie sich natürlich überschneiden. Ich werde beispielsweise verlangen, ein Ergebnis zurückzugeben, wenn der int
Wert des Parameters kleiner als 5 ist, und das andere, wenn der gerade Wert empfangen wird. In dieser Situation hat das später angegebene Verhalten Vorrang. Wenn Sie komplexe Verhaltensmuster definieren, sollten Sie daher mit den schwächsten Anforderungen (im Limit - any()
) beginnen und erst dann zu spezifischeren übergehen.
Bei der Arbeit mit Methoden mit mehr als einem Argument werden die angegebenen Anforderungen gemäß dem logischen UND kombiniert. Das heißt, um das angegebene Ergebnis zu erhalten, muss JEDES der Argumente die angegebene Anforderung erfüllen. Ich habe keinen Weg gefunden, einen willkürlichen Weg zu finden, um sie zu kombinieren, obwohl es vielleicht existiert.
Wenn man das Verhalten einer solchen Methode spezifiziert, kann man außerdem die statischen Mockito
Methoden nicht Mockito
und der direkten Übertragung von Werten kombinieren. Verwenden Sie Mockito.eq
oder Mockito.same
.
Anrufergebnisse einstellen
Nach dem Aufruf der Mock-Object-Methode muss das Objekt auf den Aufruf antworten. Die wichtigsten möglichen Konsequenzen sind die Rückgabe des Ergebnisses und das Auslösen einer Ausnahme. Genau für diese Optionen wurde das Mockito-Toolkit in erster Linie entwickelt.
Im einfachsten Fall, der oben bereits gezeigt wurde, besteht die Antwort auf den Aufruf darin, einen Wert zurückzugeben. Ich werde seinen Code noch einmal geben:
List<String> data = new ArrayList<>(); data.add("dataItem"); Mockito.when(dataService.getAllData()).thenReturn(data);
Bitte beachten Sie: Sie können nur ein Objekt zurückgeben, es gibt keine separaten Methoden für Grundelemente. Wenn die Methode einen primitiven Wert zurückgibt, tritt in einer solchen Situation ein Un / Boxing auf. In den meisten Fällen stört dies nicht, aber wenn der Compiler anders denkt, müssen Sie ihm irgendwie zustimmen ... oder seine Warnungen ertragen.
Ausnahmen zu werfen ist nicht schwerer:
Mockito.when(dataService.getDataById("invalidId")) .thenThrow(new IllegalArgumentException());
Es gibt noch einen anderen Weg: Sie können ein Ausnahmeobjekt erstellen und direkt auslösen, oder Sie können Mockito nur eine Ausnahmeklasse zur Verfügung stellen, damit es automatisch erstellt wird:
Mockito.when(dataService.getDataById("invalidId")) .thenThrow(IllegalArgumentException.class);
In beiden Fällen können Sie mit der Syntax Ausnahmen verwenden und überprüfen, aber mit Mockito können Sie einen solchen Test nicht ausführen, wenn der Ausnahmetyp nicht mit der Methode übereinstimmt, mit der ich das Auslösen dieser Ausnahme erzwingen möchte.
Bei Verwendung einer Klasse als Parameter werden Konstruktoren (auch ohne Parameter) sowie die direkte Feldinitialisierung ignoriert - das Objekt wird unter Umgehung erstellt (schließlich ist dies Mockito!), Sodass alle Felder der ausgelösten Ausnahme null
. Wenn Ihnen der Inhalt der Ausnahme wichtig ist (z. B. ein Typfeld mit einem Standardwert), müssen Sie diese Methode abbrechen und Ausnahmen manuell erstellen.
Diese Reaktionsoptionen sind geeignet, wenn Sie als Antwort auf einen Anruf mit bestimmten Bedingungen immer einen bestimmten, immer den gleichen Wert des Ergebnisses zurückgeben oder immer die gleiche Ausnahme auslösen müssen, und in den meisten Fällen sind diese Funktionen völlig ausreichend. Was aber, wenn mehr Flexibilität erforderlich ist? Angenommen, meine Methode akzeptiert eine Sammlung von Werten und gibt eine weitere Sammlung von Werten zurück, die dem ersten zu eins zugeordnet sind (z. B. Abrufen einer Sammlung von Datenobjekten anhand der Menge ihrer IDs), und ich möchte dieses Scheinobjekt wiederholt mit verschiedenen Eingabesätzen im Test verwenden Daten, jedes Mal das entsprechende Ergebnis erhalten. Sie können die Reaktion auf jeden bestimmten Parametersatz natürlich separat beschreiben, aber es gibt eine bequemere Lösung - die Mockito.thenAnswer
Methode, auch bekannt als Mockito.then
. Es akzeptiert eine Implementierung der Answer
Funktionsschnittstelle, deren einzige Methode darin besteht, ein Objekt der InvocationOnMock
Klasse zu empfangen. Von letzterem kann ich die Parameter des Methodenaufrufs anfordern (einer nach Nummer oder alle gleichzeitig in Form eines Arrays) und mit ihnen nach Belieben handeln. Sie können beispielsweise für jedes Element meiner Sammlung einen entsprechenden Wert abrufen, daraus eine neue Sammlung bilden und diese zurückgeben (Hinweis: Das gewünschte Ergebnis wird einfach zurückgegeben und nicht wie erwartet in ein Feld des Parameterobjekts geschrieben):
Mockito.when(dataService.getDataByIds(Mockito.any())) .thenAnswer(invocation -> invocation .<List<String>>getArgument(0).stream() .map(id -> { switch (id) { case "a": return "dataItemA"; case "b": return "dataItemB"; default: return null; } }) .collect(Collectors.toList()));
Ideologisch ist dies so etwas wie das Schreiben eines Modells einer realen Methode: Parameter abrufen, verarbeiten, ein Ergebnis zurückgeben. - , - , , , mock- .
Answer
, , — , AnswersWithDelay
, ReturnsElementsOf
. .
: InvocationOnMock
— Object[]
, generic-.
— thenCallRealMethod
. . mock-, spy-. mock , , - null
. spy thenCallRealMethod
spy ; , - .
thenAnswer
: InvocationOnMock
callRealMethod()
— , "" - .
OngoingStubbing
OngoingStubbing
, , , . , . thenReturn
thenThrow
, varargs. .
Mockito.when(dataService.getDataById("a")) .thenReturn("valueA1", "valueA2") .thenThrow(IllegalArgumentException.class);
"valueA1
, — "valueA2
( ), ( ) IllegalArgumentException
.
: (mock' ), . , : , , . verify
.
, , :
Mockito.verify(dataService).getDataById(Mockito.any());
, getDataById
, , . , Mockito, when
, , , mock-. , , , when
, — mock', (. ).
:
Mockito.verify(dataService, Mockito.times(1)) .getDataById(Mockito.any());
Mockito.times
; Mockito.never
. Mockito.atLeast
( Mockito.atLeastOnce
1) Mockito.atMost
, , Mockito.only
, , mock- (. . ).
, Mockito
, VerificationAfterDelay
VerificationWithTimeout
, Mockito.after
Mockito.timeout
. Zum Beispiel:
Mockito.verify(dataService, Mockito.after(1000).times(1)) .getDataById(Mockito.any());
, mock , , , . . after
timeout
, , , — , . , timeout
— . VerificationWithTimeout
never
atMost
: .
, Mockito.any()
. , , — Mockito , , . Mock- , , , , :
dataService.getDataById("a"); dataService.getDataById("b"); Mockito.verify(dataService, Mockito.times(2)).getDataById(Mockito.any()); Mockito.verify(dataService, Mockito.times(1)).getDataById("a"); Mockito.verify(dataService, Mockito.never()).getDataById("c"); dataService.getDataById("c"); Mockito.verify(dataService, Mockito.times(1)).getDataById("c"); Mockito.verifyNoMoreInteractions(dataService);
verifyNoMoreInteractions
( verifyZeroInteractions
) — - ( verify
) mock- — . : varargs, , , !
, , , . , InOrder
:
InOrder inOrder = Mockito.inOrder(dataService);
varargs; — mock- , InOrder
. verify
, Mockito.verify
:
inOrder.verify(dataService, times(2)).saveData(any()); inOrder.verify(dataService).getData();
, saveData
, — getData
. , InOrder
, — .
, , — , . - , , — , , . ArgumentCaptor
capture()
. Zum Beispiel:
DataSearchRequest request = new DataSearchRequest("idValue", new Date(System.currentTimeMillis()), 50); dataService.getDataByRequest(request); ArgumentCaptor<DataSearchRequest> requestCaptor = ArgumentCaptor.forClass(DataSearchRequest.class); Mockito.verify(dataService, times(1)).getDataByRequest(requestCaptor.capture()); assertThat(requestCaptor.getAllValues()).hasSize(1); DataSearchRequest capturedArgument = requestCaptor.getValue(); assertThat(capturedArgument.getId()).isNotNull(); assertThat(capturedArgument.getId()).isEqualTo("idValue"); assertThat(capturedArgument.getUpdatedBefore()).isAfterYear(1970); assertThat(capturedArgument.getLength()).isBetween(0, 100);
ArgumentCaptor
, , ArgumentCaptor
. getValue()
, getAllValues()
— . , , .
Mock- Mockito
, mock- , — @Mock
- :
MockitoAnnotations.initMocks(this);
( , mock', )
spy @Spy
— @Mock
… spy , , ? , — spy .
@Captor
ArgumentCaptor
— , , .
@InjectMocks
. - Mockito, . mock- , . , . - , null
, - . ( ) dependency injection.
Mockito
, : mock (spy, argument captor...), , , . , mock' — , . JUnit , , TestNG — . , , mock' , , , . . , , — , .
, mock- . TestNG @BeforeMethod
( @AfterMethod
). mock' , , ( JUnit — @Before
).
, , — Mockito.reset
Mockito.clearInvocations
. varargs, mock'. , . : (, ) , / mock' , — . , mock' . . , , .
(, ) — MockitoAnnotations.initMocks(this);
. "" , Mockito.
— Mockito. . mock- , ( mock' ). , MockitoSession
, . TestNG:
@Mock DataService dataService; MockitoSession session; @BeforeMethod public void beforeMethod() { session = Mockito.mockitoSession() .initMocks(this) .startMocking(); } @Test public void testMethod() {
, — , "" (, ) , .
?
Mockito: mock spy-, . , . , , :
- Mockito mock-
MockSettings
( — , mock' - ); - mock-,
MockingDetails
; BDDMockito
Mockito
;- ( JUnit Mockito, ).
Mockito . javadoc' Mockito
.
, , .