2011-12-04 21 views
5

Quiero verificar el orden específico de los caracteres para garantizar que no se distorsionen. Intenté escribirlo usando InOrder pero parece que no funciona, o al menos en Mockito 1.8.5.¿Cómo probar en Mockito para un orden específico de llamadas con los mismos argumentos?

@Test 
public void inOrderTest() throws IOException{ 
    final String message = "Hello World!\n"; 

    for(char c : message.toCharArray()) 
     mockWriter.write(c); 

    final InOrder inOrder = inOrder(mockWriter); 
    for(char c : message.toCharArray()) 
     inOrder.verify(mockWriter).write(c); 
    inOrder.verifyNoMoreInteractions(); 
} 

La prueba anterior falla con el mensaje:

Verification in order failure: 
mockWriter.write(108); 
Wanted 1 time: 
-> at  org.bitbucket.artbugorski.brainfuj.interpreter.InterpreterTest.inOrderTest(InterpreterTest.java:62) 
But was 3 times. Undesired invocation: 
-> at org.bitbucket.artbugorski.brainfuj.interpreter.InterpreterTest.inOrderTest(InterpreterTest.java:58) 

¿Cómo se escribe una prueba Mockito para eso?


EDIT: Presentado como fallo http://code.google.com/p/mockito/issues/detail?id=296

Respuesta

19

Mis disculpas a los encuestados anteriores; pero, en mi opinión, usar una Respuesta contradice una de las ideas básicas de Mockito, a saber, que el troceo y la verificación son dos procesos completamente separados. Mockito tiene características para stubbing y funciones para verificación, y los creadores de Mockito han hecho un esfuerzo para mantener los dos separados. Las respuestas están destinadas a ser cortadas; y considerando que hay algunos casos en que una Respuesta es la mejor manera de verificarlo, no creo que este sea uno de ellos.

Yo usaría un ArgumentCaptor en lugar de una respuesta. Escribiría un método como este en la clase de prueba, y luego lo llamaría "Hola mundo" como argumento. Tenga en cuenta que no he probado esto, por lo que puede contener errores tipográficos.

private void verifyCharactersWritten(String expected){ 
    ArgumentCaptor<Character> captor = ArgumentCaptor.forClass(Character.class); 
    verify(mockWriter, times(expected.length())).write(captor.capture()); 
    assertEquals(Arrays.asList(expected.toCharArray()), captor.getAllValues()); 
} 

Espero que esto ayude.

+0

Slick. No sabía acerca de esa característica del captor arg. –

+0

a veces fácilmente para verificar el resultado/salida/resultado (si es posible) luego lea este código de prueba. Quiero decir, si sabemos que debería devolvernos: "algún aguijón que esperábamos" para posibles argumentos dados. – ses

4

Verificación en orden es un concepto independiente de la cantidad de veces que se haga algo, por lo que cuando se llega a la 'L' y decirle a Mockito para comprobar que se sucedió, pasa la verificación en orden, pero falla porque la llamada 'l' se realizó tres veces, y usted (implícitamente) le dijo que esperara una sola vez. Es un capricho que he golpeado antes en Mockito, pero casi siempre que sucede, termino decidiendo que mi prueba está mal escrita, y cuando lo soluciono, el problema desaparece. En su caso, diría que es manera overkill para verificar cada carácter escrito en un escritor. Si desea verificar que un mensaje se envió correctamente, debe comparar el mensaje de entrada con el mensaje de salida. En su ejemplo, eso podría implicar el uso de un StringWriter en lugar de burlarse de un escritor. A continuación, el final de la prueba sólo se parece a

assertThat(stringWriter.toString(), equalTo(message)); 

Si realmente tiene que hacer lo que está haciendo, todo lo que puedo sugerir es la excavación en el código Mockito para ver si hay una manera para que esto ocurra y, posiblemente, presentando un informe de error para ver lo que dicen al respecto.

+0

Estoy escribiendo una máquina virtual/intérprete para E/S sucede un carácter cada una vez, no hay noción de un mensaje completo, solo una serie de caracteres individuales impresos en stdout. Estoy tratando de asegurar que durante el proceso de impresión no estoy corrompiendo nada (sí, estoy implementando mi propia memoria interna). El mensaje que intento verificar es "Hello World", así que no creo que sea exagerado probar que un mensaje como ese pueda imprimirse correctamente, carácter por carácter. :) – ArtB

+1

Hola, bueno si esta prueba es crítica, es decir, deberías probar lo que estás diciendo, estoy de acuerdo con @Ryan, deberías preferir la comparación entre la entrada y la salida. Cuando anule a su escritor, use una respuesta personalizada que agregará el carácter en un 'StringBuilder' habitual, luego compárelo con nuestra entrada' 'Hello World ''. Alternativamente, probablemente puedas escribir un marcador personalizado; algo así como 'inOrder.verify (mockWriter, times (11)). write (charsThatMatchInOrder (" Hello World "));', ** sin embargo, esta prueba podría romperse fácilmente si la cadena cambiara! ** – Brice

+0

@Brice bien el fragmento de código ser emulado/interpretado es parte de la entrada de prueba así que sí, si cambia, se espera que la prueba falle. – ArtB

0

Actualmente estoy pirateando esto con una respuesta personalizada.

final List<Integer> writtenChars = new ArrayList<>(); 
willAnswer(
     new Answer(){ 
      @Override 
      public Object answer(final InvocationOnMock invocation)throws Throwable { 
       final int arg = (int) invocation.getArguments()[0]; 
       writtenChars.add(arg); 
       return null; 
      } 
     } 
    ).given(mockWriter).write(anyInt()); 

Luego, después de ejecutar los métodos deseados, pruebo contra la cadena esperada contra la lista.

final Iterator<Integer> writtenCharItr = writtenChars.iterator(); 
for(int charInt : "Hello World!\n".toCharArray()) 
    assertThat( charInt, is(writtenCharItr.next()) ); 
assertThat("There are no more chars.", writtenCharItr.hasNext(), is(false)); 
verify(mockWriter).flush(); 

Aunque esto no funcionará si usted está interesado en más de una vez llamada de método a menos que se graba en la lista, que fue llamado método etc.


EDIT: disculpas a Brice se parece haber llegado independientemente a esta solución, excepto de forma independiente y mejor, utilizando un StringBuilder en lugar de un List, aunque, en general, una lista funciona mejor.

+0

¿Qué estaba pensando ... Un ArgumentCaptor es mucho mejor, básicamente hace lo que esta respuesta personalizada está haciendo. La respuesta de David es mucho más correcta de muchas maneras. – Brice

2

El motivo por el que Mockito funciona así es la coherencia entre la verificación en orden y la verificación regular. En otras palabras, si no lo implementamos de esta manera, la API habría sido sorprendente de una manera diferente :) Haces concesiones al intentar diseñar una API decente.

Entonces ... la respuesta. En primer lugar, debe evitar declaraciones como bucles (o condicionales) en el código de prueba. ¡La razón es que te preocupas mucho por la claridad y capacidad de mantenimiento del código de prueba! =)

Si eliminamos los bucles de la prueba, ya no tenemos un caso de uso ... Sin el caso de uso, es difícil dar una respuesta. David's ArgumentCaptor podría no ser una mala idea.

Espero que ayude!

+0

No estoy de acuerdo. Podría desenrollar el ciclo y tendría el mismo problema. Sé esto de hecho ya que lo hice cuando intentaba depurar esto. En segundo lugar, si se llama InOrder, pero no puede decir exactamente en qué orden se hicieron las llamadas, ¿no es eso una flagrante violación del principio de menor asombro? – ArtB

0

Esta es una prueba extraña, pero aún así, debe ser compatible con la API burlona. Creo que puede ser compatible con Mockito, ya que otras API de burla lo admiten.

Con Unitils Mock:

Mock<Writer> mockWriter; 

@Test 
public void inOrderTest() throws Exception { 
    Writer writer = mockWriter.getMock(); 
    final String message = "Hello World!\n"; 

    for (char c : message.toCharArray()) 
     writer.write(c); 

    for (char c : message.toUpperCase().toCharArray()) 
     mockWriter.assertInvokedInSequence().write(c); 
    MockUnitils.assertNoMoreInvocations(); 
} 

O con JMockit (mi propia herramienta):

@Test 
public void inOrderTest(final Writer mockWriter) throws Exception { 
    final String message = "Hello World!\n"; 

    for (char c : message.toCharArray()) 
     mockWriter.write(c); 

    new FullVerificationsInOrder() {{ 
     for (char c : message.toCharArray()) 
      mockWriter.write(c); 
    }}; 
} 
Cuestiones relacionadas