Jak zweryfikować wiele wywołań metod z różnymi parametrami


116

Mam następującą metodę, na której chcę sprawdzić zachowanie.

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

    ...
}

W mojej klasie @Test miałem nadzieję zrobić coś takiego, aby sprawdzić, czy errors.add()jest wywoływane z „wyjątkiem.message” i ponownie z „wyjątkiem.detail”

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

jednak Mockito narzeka w następujący sposób

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

Jak mogę powiedzieć Mockito, aby sprawdzał obie wartości?


1
jeśli masz 2 metody z różnymi podpisami, możesz napisać osobny przypadek testowy dla obu.
— Naveen Babu,

8
Tak, ale w tym przypadku jest to ta sama sygnatura metody, ale tylko inne wartości argumentów
— Brad

możesz spróbować użyćMockito.reset()
— takacsot

Odpowiedzi:


102

Dalsza lektura skłoniła mnie do wypróbowania ArgumentCaptors i następnych prac, chociaż znacznie bardziej rozwlekłych, niż bym chciał.

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

czy istnieje sposób, aby upewnić się, że pewne parametry zostały sparowane przy użyciu tego podejścia? Załóżmy na przykład, że metoda OP miała dwa argumenty i chciał sprawdzić, czy zostały one wywołane razem
— popełniono i

1
Przypadek testowy OP wywołuje methodToTest()dokładnie raz, dlatego ta odpowiedź weryfikuje, czy oba wywołania są wykonane razem. Przechwycony List<String> valueselement, który jest potwierdzany, będzie zawierał tylko dwie testowane wartości i żadnych innych. Możesz też dodać assertTrue(values.size == 2). Jeśli tego chcesz, zastąpiłbym 3 stwierdzenia assertTrue pojedynczym Hamcrestem ...assertThat(values, contains("exception.message", "exception.detail"));
— Brad,

czy OP nie wywołuje przypadku testowego metody methodToTest () dwukrotnie?
— committedandroider

przepraszam, nie było jasne. Miałem na myśli scenariusz, w którym OP chciał sprawdzić, czy dwa argumenty zostały wywołane łącznie. Zatem sygnatura metody wyglądałaby podobnie do public void methodToTest (Exception e, Message m, ActionErrors errors) {tak, aby określony wyjątek był wywoływany z określoną wiadomością. Przypuszczałem, że może po prostu mieć dwa ArgumentCaptors a następnie pobrać indeks i porównać przy użyciu wartości z tych indeksów na obu listach wartości
— committedandroider

Przypadek testowy OP wywołuje methodToTest()jeden raz. Jest to argument metody, który ActionErrors errorsjest wywoływany wewnętrznie dwukrotnie.
— Brad

61

Jeśli kolejność obu add()połączeń jest istotna, możesz użyć 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
Wystarczy przekazać pojedynczy errorsargument: InOrder inOrder = inOrder(errors);(patrz dokumentacja )
— GreenhouseVeg

2
Co się stanie, jeśli zamówienie NIE jest istotne? co często się zdarza.
— haelix

1
@haelix W takim przypadku użyj odpowiedzi Brads. Przekształcają Listsię Seti twierdzą, że zestaw wejść równa podanego zestawu przez zrzuty argument.
— zrzut ekranu

25

Spróbuj czegoś takiego:

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

5
Twój czek jest oczywiście zbyt rozluźniony.
— haelix

17

prawdopodobnie masz problem w swoim kodzie. Ponieważ w rzeczywistości piszesz ten kod:

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

Zauważ, że pierwsza weryfikacja nie jest równa w odniesieniu do rzeczywistych wywołań.

Radziłbym również, aby właściwie nie kpić z typów, których nie jesteś właścicielem, np. Typu rozpórki.

[EDYCJA @Brad]

Po uruchomieniu kodu Brice'a (powyżej) w moim IDE widzę, że użyłem ActionError zamiast ActionMessage, dlatego moja weryfikacja () nie pasuje. Komunikat o błędzie, który opublikowałem początkowo, wprowadzał mnie w błąd, myśląc, że to pierwszy niezgodny argument. Okazuje się, że był to drugi argument.

Tak więc odpowiedź na moje pytanie brzmi

/** 
 * 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
Nie rozumiem tego, co próbujesz powiedzieć. Czy kolejność weryfikacji ma znaczenie? jeśli kolejność weryfikacji ma znaczenie. Dlaczego więc tutaj jest dostępny interfejs API InOrder?
— Oleksandr Papchenko

Podobnie jak powyżej, kolejność weryfikacji jest nieistotna; dlatego jest InOrder.
— Brice

12

Możesz użyć, Mockito.atLeastOnce()który pozwala Mockito przejść test, nawet jeśli ten mockObject będzie wywoływany wiele razy.

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

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

1

1) Powiedz Mokito o całkowitym oczekiwaniu na rozmowy.

2) Powiedz Mokito, ile razy każda kombinacja parametrów była oczekiwana.

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

W podobny sposób jak @ sendon1928 możemy użyć:

Mockito.times(wantedInvocationCount)

aby upewnić się, że metoda została wywołana dokładną liczbę razy (moim zdaniem preferowane rozwiązanie). Potem możemy zadzwonić

Mockito.verifyNoMoreInteractions(mock)

Aby upewnić się, że makieta nie była dalej używana w żadnym kontekście. Pełny przykład:

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

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

Mockito.verifyNoMoreInteractions(mockObject)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.