So überprüfen Sie mehrere Methodenaufrufe mit unterschiedlichen Parametern

114

Ich habe die folgende Methode, mit der ich das Verhalten überprüfen möchte

public void methodToTest( Exception e, ActionErrors errors ) {

    ...
        errors.add( "exception.message", 
                    ActionMessageFactory.createErrorMessage(e.toString() ));

        errors.add( "exception.detail",
                    ActionMessageFactory.createErrorMessage(e.getStackTrace()[0].toString() ));

    ...
}

In meiner @ Test-Klasse hatte ich gehofft, so etwas zu tun, um zu überprüfen, ob der errors.add()Aufruf mit "exception.message" und erneut mit "exception.detail" erfolgt.

verify(errors).add(eq("exception.message"), any(ActionError.class));
verify(errors).add(eq("exception.detail"), any(ActionError.class));

Mockito beschwert sich jedoch wie folgt

Argument(s) are different! Wanted:
actionErrors.add(
    "exception.message",
    <any>
);

Actual invocation has different arguments:
actionErrors.add(
    "exception.detail",
    org.apache.struts.action.ActionError@38063806
);

Wie kann ich Mockito anweisen, nach beiden Werten zu suchen?

Brad
quelle
1
Wenn Sie zwei Methoden mit unterschiedlicher Signatur haben, können Sie für beide einen separaten Testfall schreiben.
Naveen Babu
8
Ja, aber in diesem Fall ist es die gleiche Methodensignatur, aber nur unterschiedliche Argumentwerte
Brad
Sie könnten versuchen zu verwendenMockito.reset()
Takacsot

Antworten:

101

Weiterführende Literatur hat mich veranlasst, ArgumentCaptors und die folgenden Werke zu verwenden, obwohl sie viel ausführlicher sind, als ich es gerne hätte.

ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);

verify(errors, atLeastOnce()).add(argument.capture(), any(ActionMessage.class));

List<String> values = argument.getAllValues();

assertTrue(values.contains("exception.message"));
assertTrue(values.contains("exception.detail"));
Brad
quelle
Gibt es eine Möglichkeit, um sicherzustellen, dass bestimmte Parameter mit diesem Ansatz gepaart wurden? Nehmen wir zum Beispiel die Methode der OP hatte zwei Argumente und wollten sie zusammengerufen , um zu überprüfen , wurden
committedandroider
1
Der Testfall des OP ruft methodToTest()genau einmal auf, daher bestätigt diese Antwort, dass die beiden Anrufe zusammen getätigt werden. Das erfasste List<String> valuesCapture enthält nur die beiden getesteten Werte und keine anderen. Sie könnten auch hinzufügen assertTrue(values.size == 2). Wenn Sie dies möchten, würde ich die 3 assertTrue-Anweisungen durch ein einzelnes Hamcrest ersetzen ...assertThat(values, contains("exception.message", "exception.detail"));
Brad
Ruft der Testfall von OP nicht zweimal methodToTest () auf?
committedandroider
Entschuldigung, ich war nicht klar. Ich bezog mich auf das Szenario, in dem OP testen wollte, dass zwei Argumente zusammen aufgerufen wurden. Die Methodensignatur würde also ungefähr so ​​aussehen wie public void methodToTest (Ausnahme e, Nachricht m, ActionErrors-Fehler) {, sodass eine bestimmte Ausnahme mit einer bestimmten Nachricht aufgerufen wird. Ich nahm an, dass Sie nur zwei ArgumentCaptors haben könnten und dann den Index abgerufen wird und in beiden Wertelisten bei diesem Indizes mit den Werten vergleichen
committedandroider
Der Testfall von OP wird methodToTest()einmal aufgerufen. Es ist das Methodenargument, ActionErrors errorsdas intern zweimal aufgerufen wird.
Brad
60

Wenn die Reihenfolge von beiden add() Anrufe relevant ist, können Sie Folgendes verwenden InOrder:

InOrder inOrder = inOrder(errors);
inOrder.verify(errors).add(eq("exception.message"), any(ActionError.class));
inOrder.verify(errors).add(eq("exception.detail"), any(ActionError.class));
Christoph Walesch
quelle
7
Es reicht aus, ein einziges errorsArgument zu übergeben: InOrder inOrder = inOrder(errors);(siehe Dokumente )
GreenhouseVeg
2
Was ist, wenn die Bestellung NICHT relevant ist? was oft der Fall ist.
Haelix
1
@haelix Verwenden Sie in diesem Fall Brads Antwort. Konvertieren Sie das Listin Setund versichern Sie, dass die Menge der Eingaben der Menge entspricht, die durch die Argumenterfassung angegeben wird.
Flopshot
25

Versuchen Sie so etwas:

verify(errors, times(2))
     .add(AdditionalMatchers.or(eq("exception.message"), eq("exception.detail")),
          any(ActionError.class));
John B.
quelle
4
Ihr Scheck ist offensichtlich zu entspannt.
Haelix
17

Sie haben wahrscheinlich ein Problem in Ihrem Code. Denn tatsächlich schreiben Sie diesen Code tatsächlich:

Map<Character, String> map = mock(Map.class);

map.put('a', "a");
map.put('b', "b");
map.put('c', "c");

verify(map).put(eq('c'), anyString());
verify(map).put(eq('a'), anyString());
verify(map).put(eq('b'), anyString());

Beachten Sie, dass die erste Überprüfung nicht einmal in Bezug auf die tatsächlichen Aufrufe erfolgt.

Außerdem würde ich Ihnen empfehlen, keine Typen zu verspotten, die Sie nicht besitzen, z. B. den Strebentyp.

[EDIT @Brad]

Nachdem ich Brices Code (oben) in meiner IDE ausgeführt habe, kann ich feststellen, dass ich ActionError anstelle von ActionMessage verwendet habe. Aus diesem Grund stimmte mein verify () nicht überein. Die Fehlermeldung, die ich ursprünglich gepostet habe, hat mich irregeführt, dass es das erste Argument war, das nicht übereinstimmte. Es stellte sich heraus, dass es das zweite Argument war.

Die Antwort auf meine Frage lautet also

/** 
 * note that ActionMessageFactory.createErrorMessage() returns ActionMessage
 * and ActionError extends ActionMessage
 */
verify(errors).add(eq("exception.message"), any(ActionMessage.class));
verify(errors).add(eq("exception.detail"), any(ActionMessage.class));
Brice
quelle
1
Verstehe nicht, was du sagen willst. Ist die Reihenfolge der Überprüfung wichtig? wenn die Überprüfungsreihenfolge wichtig ist. Warum wird hier die InOrder-API bereitgestellt?
Oleksandr Papchenko
Genau wie das, was oben geschrieben wurde, ist die Reihenfolge der Überprüfung irrelevant. deshalb gibt es InOrder.
Brice
12

Sie können verwenden Mockito.atLeastOnce(), wodurch Mockito den Test bestehen kann, auch wenn dieses mockObject mehrmals aufgerufen wird.

Mockito.verify(mockObject, Mockito.atLeastOnce()).testMethod(Mockito.eq(1));

Mockito.verify(mockObject, Mockito.atLeastOnce()).testMethod(Mockito.eq(2));
sendon1982
quelle
1

1) Teilen Sie Mokito die gesamte Anruferwartung mit.

2) Sagen Sie Mokito, wie oft jede Parameterkombination erwartet wurde.

verify(errors, times(2)).add(any(), any(ActionMessage.class));

verify(errors, atLeastOnce()).add(eq("exception.message"), any());
verify(errors, atLeastOnce()).add(eq("exception.detail"), any());
Epox
quelle
0

Ähnlich wie bei @ sendon1928 können wir Folgendes verwenden:

Mockito.times(wantedInvocationCount)

um sicherzustellen, dass die Methode genau so oft aufgerufen wurde (meiner Meinung nach bevorzugte Lösung). Danach können wir anrufen

Mockito.verifyNoMoreInteractions(mock)

Um sicherzustellen, dass Mock in keinem Zusammenhang weiter verwendet wurde. Vollständiges Beispiel:

Mockito.verify(mockObject, Mockito.times(wantedInvocationCount)).testMethod(Mockito.eq(1));

Mockito.verify(mockObject, Mockito.times(wantedInvocationCount)).testMethod(Mockito.eq(2));

Mockito.verifyNoMoreInteractions(mockObject)
Michał Powłoka
quelle