2011-12-04 12 views
5

Desidero verificare l'ordine specifico in cui i caratteri assicurano che non vengano confusi. Ho provato a scriverlo usando InOrder ma sembra non funzionare, o almeno in Mockito 1.8.5.Come testare in Mockito per un ordine specifico di chiamate con gli stessi argomenti?

@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 prova di cui sopra non riesce con il messaggio:

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) 

Come si scrive un test Mockito per questo?


EDIT: Archiviato come bug http://code.google.com/p/mockito/issues/detail?id=296

risposta

19

Le mie scuse ai precedenti intervistati; ma a mio parere, l'uso di una risposta vola un po 'di fronte a una delle idee di base di Mockito, vale a dire che lo stub e la verifica sono due processi completamente separati. Mockito ha caratteristiche per lo stub e funzionalità per la verifica, ei produttori di Mockito si sono sforzati di tenere separati i due. Le risposte sono pensate per lo stub; e mentre ci sono alcuni casi in cui una risposta è il modo migliore per verificare, non credo che questo sia uno di questi.

Vorrei utilizzare un ArgumentCaptor anziché una risposta. Scriverei un metodo come questo nella classe di test, quindi chiamalo con "Hello world" come argomento. Si noti che non ho provato questo, quindi potrebbe contenere errori di battitura.

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

Spero che questo aiuti.

+0

Chiazza di petrolio. Non sapevo di quella caratteristica del rap cap. –

+0

a volte facilmente controllare risultato/risultato/esito (se possibile) quindi leggere questo codice di prova. Voglio dire se sappiamo che dovrebbe restituirci: "un pungiglione ci aspettavamo" per gli argomenti possibili dati. – ses

4

di verifica in ordine è un concetto distinto da quante volte qualcosa è fatto, in modo che quando si arriva al 'l' e dire Mockito per verificare che successo, passa il controllo in-order, ma fallisce perché la chiamata 'l' è stata effettuata tre volte e tu (implicitamente) hai detto di aspettarti solo una volta. È una stranezza che ho già incontrato in Mockito, ma praticamente ogni volta che succede, finisco per decidere che il mio test è scritto male, e quando lo aggiusto, il problema scompare. Nel tuo caso, direi che è il modo di overkill per verificare ogni carattere scritto su un writer. Se si desidera verificare che un messaggio sia stato inviato correttamente, è necessario confrontare il messaggio di input con il messaggio di output. Nel tuo esempio, ciò potrebbe comportare l'uso di un StringWriter invece di deridere uno scrittore. Poi la fine del test appena sembra

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

Se si hanno veramente a fare quello che stai facendo, tutto quello che posso suggerire è scavare nel codice Mockito per vedere se c'è un modo per farlo accadere e, eventualmente, presentando una segnalazione di bug per vedere cosa dicono a riguardo.

+0

Sto scrivendo una macchina virtuale/interprete così ho/succede O un carattere alla un tempo, non c'è la nozione di un intero messaggio, solo una serie di singoli caratteri stampati su stdout. Sto cercando di assicurarmi che nel corso della stampa non sto corrompendo nulla (sì, sto implementando la mia memoria interna). Il messaggio che sto cercando di verificare è "Hello World", quindi non penso che sia eccessivo provare che un messaggio del genere possa essere stampato correttamente, carattere per carattere. :) – ArtB

+1

Ciao, beh, se questo test è critico, cioè dovresti testare ciò che stai emtting, sono d'accordo con @Ryan, dovresti preferire il confronto tra l'input e l'output. Quando si esegue lo stub del proprio writer, utilizzare una risposta personalizzata che aggiungerà il carattere in un normale 'StringBuilder', quindi confrontarlo con l'input' 'Hello World ''. In alternativa puoi probabilmente scrivere un matcher personalizzato; qualcosa come 'inOrder.verify (mockWriter, times (11)). write (charsThatMatchInOrder (" Hello World "));', ** tuttavia questo test potrebbe rompersi facilmente se la stringa cambia! ** – Brice

+0

@Brice bene lo snippet di codice essere emulato/interpretato fa parte dell'input del test quindi sì, se cambia, è previsto che il test fallisca. – ArtB

0

Attualmente sto violando questo problema con una risposta personalizzata.

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

Quindi, dopo aver eseguito i metodi desiderati, eseguo il test rispetto alla stringa attesa rispetto all'elenco.

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

Anche se questo non funziona, se siete interessati a più di una volta chiamata di metodo a meno che non si registra nella lista quale metodo stato chiamato ecc


EDIT: scuse a Brice voi sembra essere arrivato indipendentemente a questa soluzione, tranne che indipendentemente e meglio utilizzando uno StringBuilder invece di uno List, sebbene per un caso generale un elenco funzioni meglio.

+0

Cosa stavo pensando ... Un ArgumentCaptor è molto meglio, fondamentalmente fa ciò che questa risposta personalizzata sta facendo. La risposta di David è molto più corretta in molti modi. – Brice

2

Il motivo per cui Mockito funziona in questo modo è la coerenza tra la verifica in ordine e la verifica periodica. In altre parole, se non l'avessimo implementato in questo modo, l'API sarebbe stata sorprendente in un modo diverso :) Esegui compromessi quando cerchi di progettare una decente api.

Quindi ... la risposta. In primo luogo, dovresti evitare istruzioni come loop (o condizionali) nel codice di test. La ragione è che ti importa molto per la chiarezza e la manutenibilità del codice di prova! =)

Se rimuoviamo i loop dal test non abbiamo più un caso d'uso, anche se ... Senza il caso d'uso è difficile dare una risposta. L'ArgumentCaptor di David potrebbe non essere una cattiva idea.

Spero che questo aiuti!

+0

Non sono d'accordo. Potrei srotolare il ciclo e avrei lo stesso problema. Lo so per un dato di fatto come ho fatto quando ho provato a eseguire il debug di questo. In secondo luogo, se si chiama InOrder ma non si può capire in quale ordine le chiamate sono state fatte non è una violenta violazione del principio del minimo stupore? – ArtB

0

Questo è un test strano, ma ancora, dovrebbe essere supportato dall'API di simulazione. Credo che possa essere supportato da Mockito, poiché altre API di simulazione lo supportano.

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 (il mio strumento):

@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); 
    }}; 
} 
Problemi correlati