Przykład argumentu Mockito Captor

141

Czy ktoś może mi podać przykład pokazujący, jak używać tej org.mockito.ArgumentCaptorklasy i czym różni się ona od prostych dopasowań, które są dostarczane z mockito.

Przeczytałem dostarczone dokumenty mockito, ale te nie ilustrują tego jasno, żaden z nich nie jest w stanie wyjaśnić tego jasno.

Ujjwal
źródło

Odpowiedzi:

187

Co więcej, zgadzam się z tym, co powiedział @fge. Spójrzmy na przykład. Rozważ, że masz metodę:

class A {
    public void foo(OtherClass other) {
        SomeData data = new SomeData("Some inner data");
        other.doSomething(data);
    }
}

Teraz, jeśli chcesz sprawdzić wewnętrzne dane, możesz użyć porywacza:

// Create a mock of the OtherClass
OtherClass other = mock(OtherClass.class);

// Run the foo method with the mock
new A().foo(other);

// Capture the argument of the doSomething function
ArgumentCaptor<SomeData> captor = ArgumentCaptor.forClass(SomeData.class);
verify(other, times(1)).doSomething(captor.capture());

// Assert the argument
SomeData actual = captor.getValue();
assertEquals("Some inner data", actual.innerData);
Slava Shpitalny
źródło
Jeśli ulegnie doSomething(data)mutacji innerData, to czy ta zmiana będzie obecna w assertEquals("Some inner data", actual.innerData), czy zostanie innerDatauchwycona tak, jak jest, zanim doSomething zostanie wykonana?
Cory Klein,
@CoryKlein To OtherClassjest makieta, a ponieważ jest teraz zdefiniowana, doSomething()faktycznie nic nie zrobi, po prostu rejestruje przekazany obiekt. Oznacza to, że zostanie przechwycony tak, jak jest przed doSomethingwykonaniem.
Slava Shpitalny
3
W verify, times(1)jest wartością domyślną i można ją pominąć.
Inego
skąd ArgumentCaptor wie, że wystąpiło foo (other), skoro jest tworzone dopiero po wywołaniu foo (other)?
AvramPop
1
@AvramPop ten, który wie, że to pozorowany obiekt. Zawiera wiele informacji o makiecie. Wewnątrz wszystkich tych informacji zawiera również historię wywołań dla każdej metody wraz z jej parametrami. Kiedy wywołujesz verifymetodę, wykorzystuje te informacje do sprawdzania dopasowań względem przeprowadzanej weryfikacji. Dla każdego parametru pyta, czy pasuje do określonego wywołania, które sprawdza. Gdy ArgumentCaptor jest zaznaczony, po prostu przechowuje wartości, z którymi został wywołany, więc po verifyzakończeniu przechowuje wszystkie odpowiednie wywołania. Z grubsza tak to działa. Mam nadzieję, że to pomoże
Slava Shpitalny
35

Dwie główne różnice to:

  • kiedy wychwytujesz choćby jeden argument, jesteś w stanie wykonać znacznie bardziej rozbudowane testy na tym argumencie, z bardziej oczywistym kodem;
  • ArgumentCaptormoże przechwycić więcej niż jeden raz.

Aby zilustrować to drugie, powiedzmy, że masz:

final ArgumentCaptor<Foo> captor = ArgumentCaptor.forClass(Foo.class);

verify(x, times(4)).someMethod(captor.capture()); // for instance

Wtedy porywacz będzie mógł dać ci dostęp do wszystkich 4 argumentów, na których możesz następnie oddzielnie wykonywać asercje.

W rzeczywistości ta lub dowolna liczba argumentów, ponieważ a VerificationModenie ogranicza się do ustalonej liczby wywołań; w każdym razie porywacz da ci dostęp do nich wszystkich, jeśli chcesz.

Ma to również tę zaletę, że takie testy są (imho) dużo łatwiejsze do napisania niż konieczność implementacji własnego ArgumentMatchers - szczególnie jeśli połączysz mockito z assertj.

Aha, i rozważ użycie TestNG zamiast JUnit.

fge
źródło
1
Co się stanie, jeśli do metody zostanie przekazanych wiele parametrów - wszystkie z różnych typów? Na przykład, jak faktycznie sprawdzić, czy parametr boolowski był prawdziwy .
Igor Ganapolsky
21
Czy możesz wyjaśnić swój komentarz: Och, i rozważ użycie TestNG zamiast JUnit. . Dlaczego warto to rozważyć? Dlaczego zmieniać?
Navigatron
1
@IgorGanapolsky, po prostu dodaj kolejny ArgumentCaptor. ArgumentCaptor <BigDecimal> arg = ArgumentCaptor.forClass (BigDecimal.class); ArgumentCaptor <String> arg2 = ArgumentCaptor.forClass (String.class); Michael michael = nowy Michael (); michael.sayHi (j); zweryfikować (j) .saySomething (arg.capture (), arg2.capture ()); System.out.println ("wartość to" + arg.getValue ()); System.out.println ("string to" + arg2.getValue ());
johnwick0831
12

Aby przeprowadzić pełną kontrolę, należy wykonać następujące czynności:

Przygotuj porywacza:

ArgumentCaptor<SomeArgumentClass> someArgumentCaptor = ArgumentCaptor.forClass(SomeArgumentClass.class);

zweryfikuj wywołanie zależnego od komponentu (współpracownika badanego podmiotu) razy (1), jest wartością domyślną, więc nie trzeba go dodawać.

verify(dependentOnComponent, times(1)).send(someArgumentCaptor.capture());

Przekaż argument współpracownikowi

SomeArgumentClass someArgument = messageCaptor.getValue();

someArgument może służyć do potwierdzeń

Lho Ben
źródło
-2

Tutaj podam właściwy przykład jednej metody wywołania zwrotnego. więc załóżmy, że mamy metodę taką jak login ():

 public void login() {
    loginService = new LoginService();
    loginService.login(loginProvider, new LoginListener() {
        @Override
        public void onLoginSuccess() {
            loginService.getresult(true);
        }

        @Override
        public void onLoginFaliure() {
            loginService.getresult(false);

        }
    });
    System.out.print("@@##### get called");
}

Umieściłem tutaj również całą klasę pomocniczą, aby przykład był bardziej przejrzysty: klasa loginService

public class LoginService implements Login.getresult{
public void login(LoginProvider loginProvider,LoginListener callback){

    String username  = loginProvider.getUsername();
    String pwd  = loginProvider.getPassword();
    if(username != null && pwd != null){
        callback.onLoginSuccess();
    }else{
        callback.onLoginFaliure();
    }

}

@Override
public void getresult(boolean value) {
    System.out.print("login success"+value);
}}

i mamy listener LoginListener jako:

interface LoginListener {
void onLoginSuccess();

void onLoginFaliure();

}

teraz chciałem tylko przetestować metodę login () klasy Login

 @Test
public void loginTest() throws Exception {
    LoginService service = mock(LoginService.class);
    LoginProvider provider = mock(LoginProvider.class);
    whenNew(LoginProvider.class).withNoArguments().thenReturn(provider);
    whenNew(LoginService.class).withNoArguments().thenReturn(service);
    when(provider.getPassword()).thenReturn("pwd");
    when(provider.getUsername()).thenReturn("username");
    login.getLoginDetail("username","password");

    verify(provider).setPassword("password");
    verify(provider).setUsername("username");

    verify(service).login(eq(provider),captor.capture());

    LoginListener listener = captor.getValue();

    listener.onLoginSuccess();

    verify(service).getresult(true);

nie zapomnij również dodać adnotacji nad klasą testową jako

@RunWith(PowerMockRunner.class)
@PrepareForTest(Login.class)
Vikram singh
źródło
1
Czy nie powinno odnosić się do ArgumentCaptor?
Felipe Martins Melo
Tak jesteśmy przechwytywania słuchacza przekazany do metody login () w przykładzie login (LoginProvider loginProvider, LoginListener callback)
Vikram Singh
Gdzie jest captorzdefiniowane w Twojej odpowiedzi?
tom_mai78101
ArgumentCaptor <LoginListener> listenerCaptor = ArgumentCaptor.forClass (LoginListener.class);
Vikram singh