Ejemplo del argumento de MockitoCaptor

141

¿Alguien puede proporcionarme un ejemplo que muestre cómo usar la org.mockito.ArgumentCaptorclase y cómo es diferente de los simples matchers que se proporcionan con mockito?

Leí los documentos simulados proporcionados, pero esos no lo ilustran claramente, ninguno de ellos puede explicarlo con claridad.

Ujjwal
fuente

Respuestas:

187

Estoy de acuerdo con lo que dijo @fge, más terminado. Veamos un ejemplo. Considera que tienes un método:

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

Ahora, si desea verificar los datos internos, puede usar el captor:

// 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
fuente
Si doSomething(data)muta innerData, ¿ese cambio estará presente assertEquals("Some inner data", actual.innerData)o se innerDatacapturará tal cual antes de doSomething ejecutarse?
Cory Klein
@CoryKlein The OtherClasses un simulacro, y como se define ahora doSomething(), en realidad no hará nada, simplemente registra el objeto que se pasó. Esto significa que se capturará tal cual antes de doSomethingejecutarse.
Slava Shpitalny
3
En verify, times(1)es el valor predeterminado y se puede omitir.
Inego
¿Cómo sabe ArgumentCaptor que foo (otro) sucedió ya que se instancia solo después de la llamada foo (otro)?
AvramPop
1
@AvramPop el que sabe esto es el objeto simulado. Contiene mucha información sobre el simulacro. Dentro de toda esa información también contiene el historial de llamadas para cada método con sus parámetros. Cuando llama al verifymétodo, utiliza esa información para ejecutar coincidencias con la verificación que está haciendo. Para cada parámetro, pregunta si coincide con la llamada específica que verifica. Cuando se marca ArgumentCaptor, simplemente almacena los valores con los que se invocó para que, cuando verifyfinalice, contenga todas las invocaciones relevantes. Es más o menos cómo funciona. Espero que ayude
Slava Shpitalny
35

Las dos diferencias principales son:

  • cuando captura incluso un solo argumento, puede hacer pruebas mucho más elaboradas sobre este argumento y con un código más obvio;
  • una ArgumentCaptorpuede capturar más de una vez.

Para ilustrar esto último, diga que tiene:

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

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

Luego, el captor podrá darle acceso a los 4 argumentos, sobre los cuales puede realizar aserciones por separado.

Esto o cualquier número de argumentos, de hecho, ya que un VerificationMode no se limita a un número fijo de invocaciones; En cualquier caso, el captor le dará acceso a todos ellos, si lo desea.

Esto también tiene el beneficio de que tales pruebas son (en mi humilde opinión) mucho más fáciles de escribir que tener que implementar las suyas propias. ArgumentMatcher s, particularmente si combina mockito con afirmación.

Ah, y considere usar TestNG en lugar de JUnit.

fge
fuente
1
¿Qué sucede si se pasan múltiples parámetros al método, todos de diferentes tipos? ¿Cómo se verifica que el parámetro booleano sea verdadero , por ejemplo?
IgorGanapolsky
21
¿Puede proporcionar una explicación para su comentario? Ah, y considere usar TestNG en lugar de JUnit. . ¿Por qué considerarlo? ¿Por qué cambiar?
Navigatron
1
@IgorGanapolsky solo agrega otro ArgumentCaptor. ArgumentCaptor <BigDecimal> arg = ArgumentCaptor.forClass (BigDecimal.class); ArgumentCaptor <String> arg2 = ArgumentCaptor.forClass (String.class); Michael michael = nuevo Michael (); michael.sayHi (j); verificar (j) .saySomething (arg.capture (), arg2.capture ()); System.out.println ("el valor es" + arg.getValue ()); System.out.println ("la cadena es" + arg2.getValue ());
johnwick0831
12

Los pasos para hacer una verificación completa son:

Prepara al captor:

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

verificar que la llamada a dependiente del componente (colaborador del sujeto bajo prueba) veces (1), es el valor predeterminado, por lo que no es necesario agregarlo.

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

Haz que el argumento pase al colaborador

SomeArgumentClass someArgument = messageCaptor.getValue();

someArgument puede usarse para afirmaciones

Lho Ben
fuente
-2

Aquí te estoy dando un ejemplo adecuado de un método de devolución de llamada. así que supongamos que tenemos un método como 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");
}

También puse toda la clase auxiliar aquí para aclarar el ejemplo: clase 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);
}}

y tenemos oyente LoginListener como:

interface LoginListener {
void onLoginSuccess();

void onLoginFaliure();

}

ahora solo quería probar el método login () de la clase 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);

Tampoco olvide agregar anotaciones sobre la clase de prueba como

@RunWith(PowerMockRunner.class)
@PrepareForTest(Login.class)
Vikram singh
fuente
1
¿No debería referirse a ArgumentCaptor?
Felipe Martins Melo
sí, estamos capturando el oyente pasado al método login () en ejemplo login (LoginProvider loginProvider, LoginListener callback)
Vikram singh
¿Dónde está el captordefinido en tu respuesta?
tom_mai78101
ArgumentCaptor <LoginListener> listenerCaptor = ArgumentCaptor.forClass (LoginListener.class);
Vikram singh