devoluciones de llamada de mockito y obtención de valores de argumentos

88

¡No tengo suerte para que Mockito capture valores de argumentos de función! Me estoy burlando de un índice de motor de búsqueda y en lugar de construir un índice, solo estoy usando un hash.

// Fake index for solr
Hashmap<Integer,Document> fakeIndex;

// Add a document 666 to the fakeIndex
SolrIndexReader reader = Mockito.mock(SolrIndexReader.class);

// Give the reader access to the fake index
Mockito.when(reader.document(666)).thenReturn(document(fakeIndex(666))

No puedo usar argumentos arbitrarios porque estoy probando los resultados de las consultas (es decir, qué documentos devuelven). Del mismo modo, no quiero especificar un valor específico y tener una línea para cada documento.

Mockito.when(reader.document(0)).thenReturn(document(fakeIndex(0))
Mockito.when(reader.document(1)).thenReturn(document(fakeIndex(1))
....
Mockito.when(reader.document(n)).thenReturn(document(fakeIndex(n))

Miré la sección de devoluciones de llamada en la página Uso de Mockito . Desafortunadamente, no es Java y no pude hacer que mi propia interpretación de eso funcione en Java.

EDITAR (para aclarar): ¿Cómo consigo que Mockito capture un argumento X y lo pase a mi función? Quiero que el valor exacto (o referencia) de X se pase a la función.

No quiero enumerar todos los casos, y el argumento arbitrario no funcionará porque estoy probando diferentes resultados para diferentes consultas.

La página de Mockito dice

val mockedList = mock[List[String]]
mockedList.get(anyInt) answers { i => "The parameter is " + i.toString } 

Eso no es Java, y no sé cómo traducir a Java o pasar lo que sea que haya pasado a una función.

nflacco
fuente
No estoy seguro de entender exactamente qué es lo que te está fallando. Tu llamada a Mockito.when(reader.document(666)).thenReturn(document(fakeIndex(666))debería configurar el objeto simulado por ti. ¿Qué pasa cuando llamas reader.document(666)?
altamente cafeinado
El 666 funciona bien. Sin embargo, me gustaría poder pasar un número específico X y obtener el resultado de fakeIndex (X). Tengo una gran cantidad de documentos potenciales para probar las consultas y no quiero ingresarlos todos.
nflacco

Respuestas:

103

Nunca he usado Mockito, pero quiero aprender, así que aquí va. Si alguien menos despistado que yo responde, ¡prueba su respuesta primero!

Mockito.when(reader.document(anyInt())).thenAnswer(new Answer() {
 public Object answer(InvocationOnMock invocation) {
     Object[] args = invocation.getArguments();
     Object mock = invocation.getMock();
     return document(fakeIndex((int)(Integer)args[0]));
     }
 });
Ed Staub
fuente
2
Acabo de notar el enlace en el lado derecho a Mockito: Cómo hacer que un método devuelva un argumento que se le pasó . Parece que estoy cerca, si no en el clavo.
Ed Staub
¡Fuerte reputación de usuario (666) con la correlación de la pregunta original! Eso funcionó muy bien. El único cambio que hice para obtener la compilación de cosas se puso público frente a la respuesta del objeto (invocación de InvocationOnMock) ....
nflacco
54

Consulte ArgumentCaptors:

https://site.mockito.org/javadoc/current/org/mockito/ArgumentCaptor.html

ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
Mockito.when(reader.document(argument.capture())).thenAnswer(
  new Answer() {
    Object answer(InvocationOnMock invocation) {
      return document(argument.getValue());
    }
  });
clasificatorio
fuente
3
Vaya, no sabía que pudieras usar ArgumentCaptors para cortar. Sin embargo, hay una gran advertencia al respecto en ese enlace. Proceda con precaución.
inocente
3
Sí, tienes razón. Los captores solo deben usarse con verificaciones.
clasificatorio
41

Es posible que desee utilizar verify () en combinación con ArgumentCaptor para asegurar la ejecución en la prueba y ArgumentCaptor para evaluar los argumentos:

ArgumentCaptor<Document> argument = ArgumentCaptor.forClass(Document.class);
verify(reader).document(argument.capture());
assertEquals(*expected value here*, argument.getValue());

Obviamente, se puede acceder al valor del argumento a través de argument.getValue () para una mayor manipulación / verificación o lo que desee hacer.

fl0w
fuente
2
mejor respuesta: sencillo, fácil de entender
Radu Cugut
No responde la pregunta. La pregunta es sobre Mockito.when y no verificar.
seBaka28
@ seBaka28 la mejor solución para obtener argumentos es un captor de argumentos. Se recomienda encarecidamente que los autores de Mockito utilicen ArgumentCaptors con verificación, por lo que quería dar una respuesta en perspectiva completa. si decide no usarlos, esa es su elección, pero no se recomienda. EDITAR: No veo cómo esto justifica un voto negativo, pero esa también es su elección.
fl0w
1
Porque no responde a la pregunta. Sí, ArgumentCaptor es excelente cuando desea capturar el argumento, pero no puede usarlo junto con when (...). ThenReturn () que es lo que OP está tratando de hacer: basado en un determinado parámetro, un servicio simulado se supone que devuelve un objeto específico.
seBaka28
1
@YuraHoy ese es un mensaje de error estándar cuando usa verify y el objeto o método se llama con más frecuencia de lo que le dijo a verify que esperara. Puede cambiar el recuento de expectativas agregando el argumento de tiempos (n) de la siguiente manera: verificar (lector, tiempos (5)): esto esperaría 5 interacciones. Para referencia, consulte: baeldung.com/mockito-verify
fl0w
13

Con Java 8, esto podría ser algo como esto:

Mockito.when(reader.document(anyInt())).thenAnswer(
  (InvocationOnMock invocation) -> document(invocation.getArguments()[0]));

Supongo que documentes un mapa.

Jean-François Beauchef
fuente