Exemplo do argumento de Mockito

141

Alguém pode me fornecer um exemplo mostrando como usar a org.mockito.ArgumentCaptorclasse e como ela é diferente dos pares simples fornecidos com o mockito.

Eu li os documentos mockito fornecidos, mas esses não o ilustram claramente, nenhum deles é capaz de explicá-lo com clareza.

Ujjwal
fonte

Respostas:

187

Eu concordo com o que o @fge disse, muito mais. Vamos ver o exemplo. Considere que você tem um método:

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

Agora, se você deseja verificar os dados internos, pode usar o capttor:

// 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
fonte
Se houver doSomething(data)mutação innerData, essa alteração estará presente assertEquals("Some inner data", actual.innerData)ou será innerDatacapturada como está antes de doSomething ser executada?
Cory Klein
@CoryKlein O OtherClassé uma farsa e, como está definido agora, doSomething()ele não fará nada, simplesmente registra o objeto que foi passado. Isso significa que ele será capturado como está antes de doSomethingser executado.
Slava Shpitalny
3
In verify, times(1)é o padrão e pode ser omitido.
Inego 6/09/19
como o ArgumentCaptor sabe que foo (other) aconteceu porque ele é instanciado somente após a chamada foo (other)?
AvramPop 15/07
1
@ AvramPop quem sabe que esse é o objeto simulado. Ele contém muitas informações sobre o mock. Dentro de todas essas informações, ele também contém o histórico de chamadas para cada método com seus parâmetros. Quando você chama o verifymétodo, ele usa essas informações para executar correspondências na verificação que você está fazendo. Para cada parâmetro, ele pergunta se corresponde à chamada específica que verifica. Quando o ArgumentCaptor está marcado, ele simplesmente armazena os valores com os quais foi chamado e, quando verifytermina, mantém todas as chamadas relevantes. É mais ou menos assim que funciona. Espero que ajude
Slava Shpitalny
35

As duas principais diferenças são:

  • quando você captura um único argumento, é capaz de fazer testes muito mais elaborados sobre esse argumento e com um código mais óbvio;
  • um ArgumentCaptorpode capturar mais de uma vez.

Para ilustrar o último, diga que você tem:

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

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

Em seguida, o captador poderá fornecer acesso a todos os 4 argumentos, nos quais você poderá executar asserções separadamente.

Este ou qualquer número de argumentos, de fato, uma vez que a VerificationModenão se limita a um número fixo de invocações; de qualquer forma, o seqüestrador lhe dará acesso a todos eles, se desejar.

Isso também tem o benefício de que esses testes são (mais) muito mais fáceis de escrever do que ter que implementar seus próprios ArgumentMatchers - principalmente se você combinar mockito com assertj.

Ah, e considere usar o TestNG em vez do JUnit.

fge
fonte
1
E se houver vários parâmetros passados ​​para o método - todos de tipos diferentes? Como você realmente verifica se o parâmetro booleano era verdadeiro , por exemplo.
IgorGanapolsky
21
Você pode fornecer uma explicação para o seu comentário: Ah, e considere usar o TestNG em vez do JUnit. . Por que considerar isso? Por que mudar?
precisa
1
@IgorGanapolsky, você apenas adiciona outro ArgumentCaptor. ArgumentCaptor <BigDecimal> arg = ArgumentCaptor.forClass (BigDecimal.class); ArgumentCaptor <> arg2 = ArgumentCaptor.forClass (String.class); Michael michael = novo Michael (); michael.sayHi (j); Verifique (j) .saySomething (arg.capture (), arg2.capture ()); System.out.println ("o valor é" + arg.getValue ()); System.out.println ("string é" + arg2.getValue ());
johnwick0831
12

As etapas para fazer uma verificação completa são:

Prepare o seqüestrador:

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

verifique se a chamada para dependente do componente (colaborador do sujeito em teste) vezes (1) é o valor padrão, portanto, não é necessário adicioná-lo.

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

Obter o argumento passado para o colaborador

SomeArgumentClass someArgument = messageCaptor.getValue();

someArgument pode ser usado para asserções

Lho Ben
fonte
-2

Aqui, estou fornecendo um exemplo adequado de um método de retorno de chamada. então suponha que tenhamos um método como o método 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");
}

Também coloquei toda a classe auxiliar aqui para tornar o exemplo mais claro: classe 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);
}}

e temos o ouvinte LoginListener como:

interface LoginListener {
void onLoginSuccess();

void onLoginFaliure();

}

agora eu só queria testar o método login () da classe 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);

Também não se esqueça de adicionar anotações acima da classe de teste,

@RunWith(PowerMockRunner.class)
@PrepareForTest(Login.class)
Vikram singh
fonte
1
Não deveria se referir ao ArgumentCaptor?
Felipe Martins Melo
Sim, nós estamos capturando o ouvinte passado para o login () método no exemplo login (LoginProvider loginProvider, LoginListener callback)
Vikram Singh
Onde está o captordefinido na sua resposta?
tom_mai78101 01/04
ArgumentCaptor <LoginListener> listenerCaptor = ArgumentCaptor.forClass (LoginListener.class);
Vikram singh