Mockito. Sprawdź argumenty metody

220

Googlowałem na ten temat, ale nie znalazłem nic istotnego. Mam coś takiego:

Object obj = getObject();
Mockeable mock= Mockito.mock(Mockeable.class);
Mockito.when(mock.mymethod(obj )).thenReturn(null);

Testeable testableObj = new Testeable();
testableObj.setMockeable(mock);
command.runtestmethod();

Teraz chcę sprawdzić, czy to mymethod(Object o), co nazywa się wewnątrz runtestmethod(), zostało wywołane z Obiektem o, a nie z żadnym innym. Ale zawsze zdaję test, niezależnie od tego, co poddam weryfikacji, na przykład:

Mockito.verify(mock.mymethod(Mockito.eq(obj)));

lub

Mockito.verify(mock.mymethod(Mockito.eq(null)));

lub

Mockito.verify(mock.mymethod(Mockito.eq("something_else")));

Zawsze zdaję test. Jak mogę przeprowadzić tę weryfikację (jeśli to możliwe)?

Dziękuję Ci.

manolowar
źródło

Odpowiedzi:

334

Alternatywą dla ArgumentMatcherjest ArgumentCaptor.

Oficjalny przykład:

ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
verify(mock).doSomething(argument.capture());
assertEquals("John", argument.getValue().getName());

Captor można również zdefiniować za pomocą adnotacji @Captor :

@Captor ArgumentCaptor<Person> captor;
//... MockitoAnnotations.initMocks(this);
@Test public void test() {
    //...
    verify(mock).doSomething(captor.capture());
    assertEquals("John", captor.getValue().getName());
}
eugene82
źródło
1
Dzięki za próbkę! Nigdy go nie użyłem. To trochę dziwne mieć takie rzeczy jak captor w kodzie, ale to pomogło.
Artemis
1
Haha, nie zrozumiałem pytania, ale odpowiedź bardzo mi pomogła. Dzięki :-)
Marcus K.,
13
Ważne: Wywołaj Verify () / capture () po użyciu makiety. Myślałem, że trzeba go „zainstalować” przed ...
Daniel Alder,
1
Dzięki za tę odpowiedź!
Jose Flavio Quispe Irrazábal,
To świetna odpowiedź !! Dziękuję Ci bardzo!
Ulky Igor
61

Czy próbujesz osiągnąć logiczną równość, korzystając z metody .equals obiektu? Możesz to zrobić, korzystając z argumentu argThat, który jest zawarty w Mockito

import static org.mockito.Matchers.argThat

Następnie możesz zaimplementować własny moduł dopasowywania argumentów, który będzie odraczał metodę .equals każdego obiektu

private class ObjectEqualityArgumentMatcher<T> extends ArgumentMatcher<T> {
    T thisObject;

    public ObjectEqualityArgumentMatcher(T thisObject) {
        this.thisObject = thisObject;
    }

    @Override
    public boolean matches(Object argument) {
        return thisObject.equals(argument);
    }
}

Teraz za pomocą kodu możesz go zaktualizować, aby czytać ...

Object obj = getObject();
Mockeable mock= Mockito.mock(Mockeable.class);
Mockito.when(mock.mymethod(obj)).thenReturn(null);

Testeable obj = new Testeable();
obj.setMockeable(mock);
command.runtestmethod();

verify(mock).mymethod(argThat(new ObjectEqualityArgumentMatcher<Object>(obj)));

Jeśli chcesz uzyskać DOKŁADNĄ równość (ten sam obiekt w pamięci), po prostu zrób

verify(mock).mymethod(obj);

Spowoduje to sprawdzenie, że raz został wywołany.

Matthew Kirkley
źródło
1
W ReflectionEqualstym celu możesz użyć wbudowanej klasy.
takacsot
2
+1 za twoją odpowiedź. Ale chciałbym dodać, że verify(mock).mymethod(obj);nie sprawdza DOKŁADNEJ równości (ten sam obiekt w pamięci). Zamiast tego używa metody equals-method, która mogła zostać zastąpiona.
efux
Możesz także utworzyć anonimową implementację, ArgumentMatcheraby być mniej gadatliwym.
botchniaque
1
Więcej szczegółów: domyślnie verify()wywołuje equals()metodę / argument argumentu / inbound , a nie metodę / record object / equals(). nie ma to znaczenia, chyba że próbujesz potwierdzić, że badany obiekt zwraca określoną instancję obiektu, a podmiot zwraca zamiast tego coś, co powinno być przezroczystym dekoratorem tej instancji. Do verifyużytkownika argumentów equals()nie wiem od dekoratora; podczas gdy dekoratorzy equals()zostaną przepisani, aby tolerować oryginał. W tym przypadku test fałszywie się nie powiedzie.
Mark McKenna,
54
  • Nie potrzebujesz eqdopasowywania, jeśli nie używasz innych dopasowań.
  • Nie używasz poprawnej składni - twoje wywołanie metody powinno znajdować się poza .verify(mock). Teraz inicjujesz weryfikację wyniku wywołania metody, nie weryfikując niczego (nie wykonując wywołania metody). Dlatego wszystkie testy przebiegają pomyślnie.

Twój kod powinien wyglądać następująco:

Mockito.verify(mock).mymethod(obj);
Mockito.verify(mock).mymethod(null);
Mockito.verify(mock).mymethod("something_else");
Bozho
źródło
Próbowałem już wcześniej i teraz, aby się upewnić. Nadal mam ten sam problem, test zawsze mija.
manolowar
2
Verifeis przez odniesienie
cnexans
17

argThat plus lambda

w ten sposób możesz zawieść weryfikację argumentów:

    verify(mock).mymethod(argThat(
      (x)->false
    ));

gdzie

import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;

argThat plus zapewnia

powyższy test „powie” Expected: lambda$... Was: YourClass.toSting.... Możesz uzyskać dokładniejszą przyczynę niepowodzenia, jeśli używasz twierdzeń w lambda:

    verify(mock).mymethod(argThat( x -> {
      assertThat(x).isNotNull();
      assertThat(x.description).contains("KEY");
      return true;
    }));

ALE: TO TYLKO DZIAŁA Z 1 METODĄ WEZWANIA. Jeśli zweryfikowana metoda wywoływana jest 2 razy, mockito przekazuje wszystkie wywoływane kombinacje każdemu weryfikatorowi. Zatem mockito oczekuje, że weryfikator po cichu zwraca truejeden zestaw argumentów i false(bez wyjątków) dla innych prawidłowych wywołań. Oczekiwanie to nie stanowi problemu dla 1 wywołania metody - powinno po prostu zwrócić true 1 raz.

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;

Teraz test mówi: Expected: Obj.description to contain 'KEY'. Was: 'Actual description'. UWAGA: Użyłem assertJasercji, ale to od Ciebie zależy, która struktura asercji będzie używana.


argThat z wieloma argumentami.

Jeśli używasz argThat, wszystkie argumenty muszą być dopasowane. Na przykład:

    verify(mock).mymethod(eq("VALUE_1"), argThat((x)->false));
    // above is correct as eq() is also an argument matcher.

verify(mock).mymethod("VALUE_1", argThat((x)->false));

// above is incorrect; an exceptoin will be thrown, as the fist arg. is given without an argument matcher.

gdzie:

import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;

eq dopasowujący

najprostszym sposobem sprawdzenia, czy argument jest równy:

verify(mock).mymethod(eq(expectedValue));
// NOTE:   ^ where the parentheses must be closed.

bezpośredni argument

jeśli porównanie przez odniesienie jest dopuszczalne, przejdź do:

verify(mock).mymethod(expectedArg);
// NOTE:   ^ where the parentheses must be closed.

Przyczyną awarii oryginalnego pytanie było złe miejsce z paranthes: verify(mock.mymethod.... To było złe. Prawo byłoby:verify(mock).*

epoksydowy
źródło
1
Ten jest moją ulubioną odpowiedzią, działa i jest o wiele bardziej elegancki niż inne.
Airwavezx
11

Użyłem Mockito.verify w ten sposób

@UnitTest
public class JUnitServiceTest
{
    @Mock
    private MyCustomService myCustomService;


    @Test
    public void testVerifyMethod()
    {
       Mockito.verify(myCustomService, Mockito.never()).mymethod(parameters); // method will never call (an alternative can be pick to use times(0))
       Mockito.verify(myCustomService, Mockito.times(2)).mymethod(parameters); // method will call for 2 times
       Mockito.verify(myCustomService, Mockito.atLeastOnce()).mymethod(parameters); // method will call atleast 1 time
       Mockito.verify(myCustomService, Mockito.atLeast(2)).mymethod(parameters); // method will call atleast 2 times
       Mockito.verify(myCustomService, Mockito.atMost(3)).mymethod(parameters); // method will call at most 3 times
       Mockito.verify(myCustomService, Mockito.only()).mymethod(parameters); //   no other method called except this
    }
}
Wolny umysł
źródło
5

Czy sprawdziłeś metodę equals dla możliwej do wyśmiewania klasy? Jeśli ta zwraca zawsze true lub testujesz tę samą instancję na tej samej instancji, a metoda równości nie jest nadpisywana (a zatem sprawdza tylko referencje), wówczas zwraca true.

rit
źródło
4

Inną metodą jest użycie metody org.mockito.internal.matchers.Equals.Equals zamiast ponownego zdefiniowania jednej:

verify(myMock).myMethod((inputObject)Mockito.argThat(new Equals(inputObjectWanted)));
Nils Renaud
źródło
3

Czy próbowałeś tego z tym samym () dopasowaniem? Jak w:

verify(mockObj).someMethod(same(specificInstance));

Miałem ten sam problem. Próbowałem z matcherem eq (), a także matcherem refEq (), ale zawsze miałem fałszywie dodatnie wyniki. Gdy użyłem tego samego () modułu dopasowującego, test nie powiódł się, gdy argumenty były różnymi instancjami i przeszedł, gdy argumenty były tej samej instancji.

cbbcloud
źródło
-1

Możesz także użyć TypeSafeDiagnosingMatcher

    private Matcher<GetPackagesRequest> expectedPackageRequest(final AvailabilityRequest request) {
    return new TypeSafeDiagnosingMatcher<GetPackagesRequest>() {

        StringBuilder text = new StringBuilder(500);

        @Override
        protected boolean matchesSafely(GetPackagesRequest req, Description desc) {
            String productCode = req.getPackageIds().iterator().next().getValue();
            if (productCode.equals(request.getSupplierProductCode())) {
                text.append("ProductCode not equal! " + productCode + " , " + request.getSupplierProductCode());
                return true;
            }

            text.append(req.toString());
            return false;
        }

        @Override
        public void describeTo(Description d) {
            d.appendText(text.toString());
        }
    };
}

Następnie sprawdź to wywołanie:

Mockito.verify(client).getPackages(Mockito.argThat(expectedPackageRequest(request)));
sendon1982
źródło