2011-03-09 21 views
15

He siguientes códigos Quiero prueba:Mockito a métodos de ensayo vacíos

public class MessageService { 
    private MessageDAO dao; 

    public void acceptFromOffice(Message message) { 
     message.setStatus(0); 
     dao.makePersistent(message); 

     message.setStatus(1); 
     dao.makePersistent(message); 

    } 
    public void setDao (MessageDAO mD) { this.dao = mD; } 
} 

public class Message { 
    private int status; 
    public int getStatus() { return status; } 
    public void setStatus (int s) { this.status = s; } 

    public boolean equals (Object o) { return status == ((Message) o).status; } 

    public int hashCode() { return status; } 
} 

necesito verificar, que acceptFromOffice método realmente establece el estado a 0, que los mensajes se mantengan, a continuación, Chage su estado a 1, y luego persistir de nuevo.

Con Mockito, he tratado de hacer lo siguiente:

@Test 
    public void testAcceptFromOffice() throws Exception { 

     MessageDAO messageDAO = mock(MessageDAO.class); 

     MessageService messageService = new MessageService(); 
     messageService.setDao(messageDAO); 

     final Message message = spy(new Message()); 
     messageService.acceptFromOffice(message); 

     verify(messageDAO).makePersistent(argThat(new BaseMatcher<Message>() { 
      public boolean matches (Object item) { 
       return ((Message) item).getStatus() == 0; 
      } 

      public void describeTo (Description description) { } 
     })); 

     verify(messageDAO).makePersistent(argThat(new BaseMatcher<Message>() { 
      public boolean matches (Object item) { 
       return ((Message) item).getStatus() == 1; 
      } 

      public void describeTo (Description description) { } 
     })); 

    } 

realmente espero aquí que la verificación verificará llamar dos veces del método makePersistent con el estado de un objeto de mensaje diferente. Pero falla diciendo que

¡La (s) argumentación (es) son diferentes!

¿Alguna pista?

+0

¿Es necesario un Mockito? Podrías crear TestMessage personalizado que extienda Message en una clase MessageDAO. Si esa es una opción, puedo escribir un código para ilustrar. Si no lo es, no lo haré :) – extraneon

Respuesta

23

Probar el código no es trivial, aunque no imposible. Mi primera idea fue utilizar ArgumentCaptor, que es mucho más fácil de usar y comprender en comparación con ArgumentMatcher. Lamentablemente, la prueba aún falla; por cierto, las razones están más allá del alcance de esta respuesta, pero puedo ayudar si eso te interesa. Todavía me parece este caso de prueba lo suficientemente interesante para ser mostrado (no solución correcta):

@RunWith(MockitoJUnitRunner.class) 
public class MessageServiceTest { 

    @Mock 
    private MessageDAO messageDAO = mock(MessageDAO.class); 

    private MessageService messageService = new MessageService(); 

    @Before 
    public void setup() { 
     messageService.setDao(messageDAO); 
    } 

    @Test 
    public void testAcceptFromOffice() throws Exception { 
     //given 
     final Message message = new Message(); 

     //when 
     messageService.acceptFromOffice(message); 

     //then 
     ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class); 

     verify(messageDAO, times(2)).makePersistent(captor.capture()); 

     final List<Message> params = captor.getAllValues(); 
     assertThat(params).containsExactly(message, message); 

     assertThat(params.get(0).getStatus()).isEqualTo(0); 
     assertThat(params.get(1).getStatus()).isEqualTo(1); 
    } 

} 

Desafortunadamente la solución de trabajo requiere el uso de algo complicado Answer. En pocas palabras, en lugar de dejar que Mockito registre y verifique cada invocación, está proporcionando un método de devolución de llamada que se ejecuta cada vez que el código de prueba se ejecuta. En este método de devolución de llamada (MakePersistentCallback objeto en nuestro ejemplo) tiene acceso a los parámetros y puede modificar el valor de retorno. Se trata de un cañón pesado y se debe utilizar con cuidado:

@Test 
    public void testAcceptFromOffice2() throws Exception { 
     //given 
     final Message message = new Message(); 
     doAnswer(new MakePersistentCallback()).when(messageDAO).makePersistent(message); 

     //when 
     messageService.acceptFromOffice(message); 

     //then 
     verify(messageDAO, times(2)).makePersistent(message); 
    } 


    private static class MakePersistentCallback implements Answer { 

     private int[] expectedStatuses = {0, 1}; 
     private int invocationNo; 

     @Override 
     public Object answer(InvocationOnMock invocation) throws Throwable { 
      final Message actual = (Message)invocation.getArguments()[0]; 
      assertThat(actual.getStatus()).isEqualTo(expectedStatuses[invocationNo++]); 
      return null; 
     } 
    } 

El ejemplo no está completo, pero ahora la prueba es satisfactoria y, más importante, falla al cambiar casi cualquier cosa en la CUT. Como puede ver, se llama al método MakePersistentCallback.answer cada vez que se invoca el messageService.acceptFromOffice(message). Dentro de naswer puede realizar todas las verificaciones que desee.

NB: Use con precaución, el mantenimiento de tales pruebas puede ser problemático por decir lo menos.

+0

muchas gracias. Pero en realidad esto no es lo que necesito. Porque, es decir, si necesito verificar alguna llamada entre dos llamadas makePersistance, esto no funcionará. Quiero decir que el orden de las llamadas no se verificará, solo el número de llamadas. Llamando como: verificar (mensajeDAO) .makePersistence (mensaje); verificar (otherMock) .smth(); verificar (mensajeDAO).makePersistence (mensaje); levantará la excepción en la primera verificación que esperaba una vez, recibió 2 llamadas de makePersistence – glaz666

+1

No realmente, este código verifica que 'makePersistent' se llamó 2 veces y que la primera llamada fue con' Message' con estado 0 y una segunda con 'Message 'con estado 1. Si necesita verificar la orden, eche un vistazo a [InOrder] (http://mockito.googlecode.com/svn/branches/1.8.5/javadoc/org/mockito/InOrder.html). –

+0

Ok, tengo tu idea, pero esto es bastante incómodo para hacer ese tipo de cosas en Mockito. De acuerdo con mi experiencia en EasyMock, aquí la grabación del comportamiento será mucho más cierto, por así decirlo. De todos modos, gracias – glaz666

0

Está en efecto probando una máquina de estados. Es bastante fácil probar MessageService con algunas implementaciones personalizadas. Creo que TestMessage sería la clase más interesante.

Para permitir que el mensaje DAO/grabe la llamada persistente realicé una implementación personalizada.

No es Mockito pero es simple, y debe hacer el trabajo.

class TestMessageDAO implements MessageDAO { 
    // I have no clue what the MessageDAO does except for makePersistent 
    // which is the only relevant part here 

    public void makePersistent(Message message) { 
    if (message instanceof TestMessage) { 
     TestMessage test = (TestMessage)message; 
     test.persistCalled(); // will be recorded by TestMessage 
    } else { 
     throw RuntimeException("This test DAO does not support non-test messages"); 
    } 
    } 
} 

// Message isn't final so... 
class TestMessage extends Message { 
    enum state { 
    STARTED, STATUS0, PERSIST0, STATUS1, PERSIST1 
    } 

    public void persistCalled() { // For testing only 
    switch (state) { 
     case STATUS0: 
     state = PERSIST0; 
     break; 
     case STATUS1: 
     state = PERSIST1; 
     break; 
     default: 
     throw new RuntimeException("Invalid transition"); 
    } 
    } 

    public void setStatus(int status) { 
    switch(state) { 
     case STARTED: 
     if (status != 0) { 
      throw new IllegalArgumentException("0 required"); 
     } 
     state = STATUS0; 
     break; 
     case PERSIST0: 
     if (status != 1) { 
      throw new IllegalArgumentException("1 required"); 
     } 

     state = STATUS1; 
     break; 
     default: 
     throw new RuntimeException("Invalid transition"); 
    } 
    } 
} 

public class TestMessageService { 

    @Test 
    public void testService() { 
    MessageDAO dao = new TestMessageDAO(); 
    Message message = new TestMessage(); 
    MessageService service = new MessageService(); 
    service.setDao(dao); 
    service.acceptFromOffice(message); 
    } 

} 
Cuestiones relacionadas