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?


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"));

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));

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));

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));

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));

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());

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)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.