Mockito Verificar argumentos del método

220

Busqué en Google esto, pero no encontré nada relevante. Tengo algo como esto:

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

Ahora, quiero verificar que mymethod(Object o), que se llama dentro runtestmethod(), se llamó con el Objeto o, no con ningún otro. Pero siempre paso la prueba, lo que sea que ponga en la verificación, por ejemplo, con:

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

o

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

o

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

Siempre paso la prueba. ¿Cómo puedo lograr esa verificación (si es posible)?

Gracias.

manolowar
fuente

Respuestas:

334

Una alternativa a ArgumentMatcheres ArgumentCaptor.

Ejemplo oficial:

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

Un captor también se puede definir utilizando la anotación @Captor :

@Captor ArgumentCaptor<Person> captor;
//... MockitoAnnotations.initMocks(this);
@Test public void test() {
    //...
    verify(mock).doSomething(captor.capture());
    assertEquals("John", captor.getValue().getName());
}
eugene82
fuente
1
Gracias por la muestra! Nunca lo usé. Parece un poco extraño tener cosas como captor en el código, pero ayudó.
Artemis
1
Jaja, no entendí la pregunta, pero la respuesta me ayudó mucho. Gracias :-)
Marcus K.
13
Importante: llame a verificar () / capture () después de usar el simulacro. Estaba pensando que tiene que ser "instalado" antes ...
Daniel Alder
1
Gracias por esta respuesta!
Jose Flavio Quispe Irrazábal
¡¡Esta es una respuesta genial!! ¡Muchas gracias!
Ulky Igor
61

¿Estás tratando de hacer la igualdad lógica utilizando el método .equals del objeto? Puede hacerlo utilizando el emparejador argThat que se incluye en Mockito

import static org.mockito.Matchers.argThat

A continuación, puede implementar su propio emparejador de argumentos que diferirá para cada método .equals de objetos

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

Ahora, usando su código, puede actualizarlo para leer ...

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

Si solo busca la igualdad EXACTA (mismo objeto en la memoria), simplemente haga

verify(mock).mymethod(obj);

Esto verificará que se haya llamado una vez.

Matthew Kirkley
fuente
1
Puede usar la compilación en ReflectionEqualsclase para esos fines.
takacsot
2
+1 por tu respuesta. Pero me gustaría agregar que verify(mock).mymethod(obj);no verifica la igualdad EXACTA (mismo objeto en la memoria). En su lugar, utiliza el método equals de objetos que podría haberse anulado.
efux
También puede crear una implementación anónima de ArgumentMatcherser menos detallado.
botchniaque
1
Más detalles: por defecto verify()invoca el equals()método / argumento de entrada , en lugar del método / objeto grabado equals(). Esto es irrelevante a menos que esté tratando de confirmar que su sujeto de prueba devuelve una instancia de objeto específica, y el sujeto devuelve lo que se supone que es un decorador transparente de esa instancia. El verifyargumento equals()no sabría del decorador; mientras que el decorador equals()sería reescrito para tolerar el original. En este caso, su prueba fallará falsamente.
Mark McKenna
54
  • No necesita el eqmatcher si no utiliza otros matchers.
  • No está utilizando la sintaxis correcta: su llamada al método debe estar fuera de .verify(mock). Ahora está iniciando la verificación del resultado de la llamada al método, sin verificar nada (sin hacer una llamada al método). Por lo tanto, todas las pruebas están pasando.

Su código debe verse así:

Mockito.verify(mock).mymethod(obj);
Mockito.verify(mock).mymethod(null);
Mockito.verify(mock).mymethod("something_else");
Bozho
fuente
Lo había intentado antes, y de nuevo ahora para estar seguro. Todavía tengo el mismo problema, la prueba siempre pasa.
manolowar
2
Se verifeis por referencia
cnexans
17

argThat más lambda

así es como puede fallar la verificación de su argumento:

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

dónde

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

argThat más afirma

la prueba anterior "dirá" Expected: lambda$... Was: YourClass.toSting.... Puede obtener una causa más específica de la falla si usa afirmaciones en la lambda:

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

PERO: ESTO SOLO FUNCIONA CON 1 LLAMADA DE MÉTODO. Si el método verificado se llama 2 o más veces, mockito pasa todas las combinaciones llamadas a cada verificador. Entonces, mockito espera que su verificador regrese silenciosamente truepara uno de los argumentos establecidos y false(sin excepciones de afirmación) para otras llamadas válidas. Esa expectativa no es un problema para 1 llamada al método, solo debe devolver 1 vez verdadero.

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

Ahora la prueba dice: Expected: Obj.description to contain 'KEY'. Was: 'Actual description'. NOTA: Utilicé assertJafirmaciones, pero depende de usted qué marco de afirmación usar.


argThat con múltiples argumentos

Si usa argThat, todos los argumentos deben ser provistos con coincidencias. P.ej:

    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.

dónde:

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

eq matcher

La forma más fácil de verificar si el argumento es igual:

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

argumento directo

Si la comparación por referencia es aceptable, continúe con:

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

La causa fundamental del fracaso pregunta original era el lugar equivocado de las paranthes: verify(mock.mymethod.... Eso estuvo mal. El derecho sería:verify(mock).*

epox
fuente
1
Esta es mi respuesta favorita, funciona y es mucho más elegante que las otras.
Airwavezx
11

He usado Mockito.verify de esta manera

@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
    }
}
De mente abierta
fuente
5

¿Ha verificado el método de igualdad para la clase simulable? Si este devuelve siempre verdadero o prueba la misma instancia contra la misma instancia y el método de igualdad no se sobrescribe (y por lo tanto solo verifica las referencias), entonces devuelve verdadero.

rit
fuente
4

El otro método es usar el método org.mockito.internal.matchers.Equals.Equals en lugar de redefinir uno:

verify(myMock).myMethod((inputObject)Mockito.argThat(new Equals(inputObjectWanted)));
Nils Renaud
fuente
3

¿Lo has probado con el mismo () matcher? Como en:

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

Yo tuve el mismo problema. Lo probé con el igualador eq () así como con el igualador refEq () pero siempre tuve falsos positivos. Cuando utilicé el mismo () matcher, la prueba falló cuando los argumentos eran instancias diferentes y pasaban una vez que los argumentos eran la misma instancia.

cbbcloud
fuente
-1

También puede usar 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());
        }
    };
}

Luego verifique esa invocación:

Mockito.verify(client).getPackages(Mockito.argThat(expectedPackageRequest(request)));
sendon1982
fuente