2012-10-27 19 views
6

Sto cercando di utilizzare Mockito prendere in giro un "lettore" tipo di classe. Pensate ad un lettore di flusso di dati, ha metodi per leggere i vari tipi di dati e far progredire il puntatore interno dopo ogni lettura.modularmente sradicamento metodo consecutivo chiamate con Mockito

public interface Reader { 
    int readInt(); 
    short readShort(); 
} 

La classe sottoposta a test viene letta in varie strutture dati dal flusso di dati. Ad esempio,

public class Somethings { 
    public List<Something> somethings; 

    public Somethings(Reader reader) { 
     somethings = new List<Something>(); 
     int count = reader.readInt(); 
     for (int i=0; i<count; i++) { 
      somethings.add(readSomething(reader)); 
     } 
    } 

    private Something readSomething(Reader reader) { 
     int a = reader.readInt(); 
     short b = reader.readShort(); 
     int c = reader.readInt(); 
     return new Something(a, b, c); 
    } 
} 

E, infine, ho la mia prova:

public class SomethingsTest { 
    @Test 
    public void testSomethings() { 
     Reader reader = Mockito.mock(Reader.class); 

     readCount(reader, 2); 
     readSomething(reader, 1, 2, 3); 
     readSomething(reader, 4, 5, 6); 

     Somethings somethings = new Somethings(reader); 
     Assert.assertEqual(2, somethings.size()); 
     Assert.assertEquals(new Something(1, 2, 3), somethings.somethings.get(0)); 
     Assert.assertEquals(new Something(4, 5, 6), somethings.somethings.get(1)); 
    } 

    private void readCount(Reader reader, int count) { 
     when(reader.readInt()).thenReturn(count); 
    } 

    private void readSomething(Reader reader, int a, short b, int c) { 
     when(reader.readInt()).thenReturn(a); 
     when(reader.readShort()).thenReturn(b); 
     when(reader.readInt()).thenReturn(c); 
    } 
} 

Purtroppo, questo non funziona. reader.readInt() restituisce sempre 6 per ogni chiamata. E capisco perché ritorna 6. Questa non è la mia domanda.

ci sono due opzioni che posso pensare di risolvere i test, ma non mi piace particolarmente uno dei due.

La prima opzione sarebbe qualcosa di simile:

public class SomethingsTest { 
    @Test 
    public void testSomethings() { 
     Reader reader = Mockito.mock(Reader.class); 

     when(reader.readInt()) 
      .thenReturn(2) 
      .thenReturn(1) 
      .thenReturn(3) 
      .thenReturn(4) 
      .thenReturn(6); 
     when(reader.readShort()) 
      .thenReturn(2) 
      .thenReturn(5); 

     Somethings somethings = new Somethings(reader); 
     Assert.assertEqual(2, somethings.size()); 
     Assert.assertEquals(new Something(1, 2, 3), somethings.somethings.get(0)); 
     Assert.assertEquals(new Something(4, 5, 6), somethings.somethings.get(1)); 
    } 
} 

Questo dovrebbe funzionare, ma è molto monolitica e disordinato. È difficile vedere quale ritorno è per quale pezzo di quale struttura, perché sono tutti mescolati, senza struttura.

La seconda opzione mi viene in mente è qualcosa di simile:

public class SomethingsTest { 
    @Test 
    public void testSomethings() { 
     Reader reader = Mockito.mock(Reader.class); 

     NewOngoingStubbing readIntStub = when(reader.readInt()); 
     NewOngoingStubbing readShortStub = when(reader.readShort()); 

     readCount(readIntStub, 2); 
     readSomething(readIntStub, readShortStub, 1, 2, 3); 
     readSomething(readIntStub, readShortStub, 4, 5, 6); 

     Somethings somethings = new Somethings(reader); 
     Assert.assertEqual(2, somethings.size()); 
     Assert.assertEquals(new Something(1, 2, 3), somethings.somethings.get(0)); 
     Assert.assertEquals(new Something(4, 5, 6), somethings.somethings.get(1)); 
    } 

    private void readCount(NewOngoingStubbing readIntStub, int count) { 
     readIntStub.thenReturn(count); 
    } 

    private void readSomething(NewOngoingStubbing readIntStub, 
      NewOngoingStubbing readShortStub, int a, short b, int c) { 
     readIntStub.thenReturn(a); 
     readShortStub.thenReturn(b); 
     readIntStub.thenReturn(c); 
    } 
} 

Questo almeno mantiene la struttura di quella originale, ma di dover passare un oggetto separato per ogni chiamata di metodo che si vuole fare sul contuso l'oggetto è ... ugh.

quale sarebbe il modo più pulito per eseguire questo test? C'è qualche opzione che mi manca qui? Alcune funzionalità che posso sfruttare? Ho appena iniziato a usare Mockito stasera ... quindi mi potrebbe benissimo mancare qualcosa.

risposta

1

Quando i metodi standard per Mockito non forniscono un meccanismo per simulare il comportamento, è possibile ricorrere all'implementazione del proprio Answer. Questo è più lavoro, ma offre una maggiore flessibilità.

A seconda delle esigenze specifiche, è possibile creare ad esempio Answer che restituisce un nuovo elemento da un elenco di numeri, indipendentemente dal tipo di richiesta (int o short). La variabile readList può essere un membro a cui è possibile accedere da tutte le funzioni che si utilizzano per impostare i risultati.

final List<Integer> readList = new ArrayList<>(); 
// ... Fill readList with answers 

Answer answerFromList = new Answer() { 
    Object answer(InvocationOnMock invocation) { 
     // Remove and return first element 
     return readList.remove(0); 
    } 
} 

when(reader.readInt()).thenAnswer(answerFromList); 
when(reader.readShort()).thenAnswer(answerFromList); 

Si noti che questo Answer è un po 'come il previsto ReturnElementsOf, così si potrebbe usare che direttamente come bene.

5

Mockito fa stubbing corso in modo nativo. Il tuo primo esempio va bene, ma questo dovrebbe anche funzionare:

when(reader.readInt()).thenReturn(2, 1, 3, 4, 6); 

La documentazione perché è here.

Se si dispone di qualcosa che gestisce un'interazione particolarmente complessa, è possibile distribuire la propria classe stub. Potreste scoprire che l'inizializzazione qualche falso con dati realistici, quindi utilizzando tale, fornisce un esempio più chiaro di come le classi collaborano oltre Mockito può. Se è così, vai con chiarezza sulla convenzione. Mockito è IMO la migliore struttura di derisione là fuori, ma a volte continuo a tirare avanti la mia.

+0

Come ho già detto, so che una delle 2 alternative che ho menzionato funzionerebbe, ma non mi interessa per nessuno dei due. Cambiare semplicemente una serie di .thenReturn (1) .thenReturn (2) a .thenReturn (1, 2) non migliora in alcun modo la struttura di esso :) – JesusFreke

+0

Quindi ho suggerito di eseguire il rollover come alternativa. – Lunivore

2

È possibile creare una classe per affrontarla, ad es.

private static class ReaderStubber { 
    private final Reader reader = Mockito.mock(Reader.class); 
    private final NewOngoingStubbing readIntStub = when(reader.readInt());; 
    private final NewOngoingStubbing readShortStub = when(reader.readShort());; 

    public Reader getReader() { 
     return reader; 
    }  

    private void readCount(int count) { 
     readIntStub.thenReturn(count); 
    } 

    private void readSomething(int a, short b, int c) { 
     readIntStub.thenReturn(a); 
     readShortStub.thenReturn(b); 
     readIntStub.thenReturn(c); 
    } 
} 

Ma allora la domanda è: hai davvero bisogno di farlo con Mockito? Non tutto dovrebbe essere deriso. Forse solo l'implementazione di uno stub Reader per il test con alcuni List<Integer> all'interno è migliore.


(Edit) Inoltre, se questo è possibile, forse si dovrebbe ridisegnare il Reader per renderlo immutabile e tornare un po 'NewOngoingReading.Spesso (ma non sempre) le cose difficili da testare sono migliori da riprogettare. Inoltre, non sarà necessario gestire la sincronizzazione.

+0

Hmm, buona idea, anche se hai un punto riguardo se usare Mockito ne vale la pena in questo caso. Probabilmente andrei con qualcosa di più simile a: private void readInt (int val) {readIntStub.thenReturn (val); }. – JesusFreke

Problemi correlati