2013-10-09 14 views
8

È un odore di codice per spiare un oggetto sottoposto a test dell'unità? Ad esempio, supponiamo di avere una classe LineCounter il cui compito è semplicemente contare il numero di righe in una stringa. -Mockito Spy'ing sull'oggetto sottoposto a test di unità

class LineCounter { 
    public int getNumLines(String string) { 
     String metadata = getStringMetadata(string); 

     // count lines in file 
     return numLines; 
    } 

    /** Expensive operation */ 
    protected String getStringMetadata(String string) { 
     // do stuff with string 
    } 
} 

Ora voglio scrivere un test JUnit 4 per questo per testare il metodo getNumLines mentre beffardo il costoso getStringMetadata chiamata. Decido di utilizzare il meccanismo di spionaggio di Mockito per fare in modo che getStringMetadata restituisca un valore fittizio.

class LineCounterTests { 
    @Test public void testGetNumLines() { 
     LineCounter lineCounterSpy = Mockito.spy(new LineCounter()); 

     // Mock out expensive call to return dummy value.    
     Mockito.when(lineCounterSpy.getStringMetadata(Mockito.anyString()).thenReturn("foo"); 

     assertEquals(2, lineCounterSpy.getNumLines("hello\nworld"); 
    } 
} 

È una cosa ragionevole da fare? Mi sento piuttosto strano testare un oggetto Spy piuttosto che la classe reale, ma non riesco davvero a pensare a un motivo contrario.

+4

Questo potrebbe essere un caso di test che guida un miglioramento del codice. Sembra che il compito di ottenere i metadati della stringa debba essere estratto in un'altra classe, a cui viene poi demandato 'LineCounter'. A quel punto hai creato una "cucitura" e puoi deridere quella dipendenza in un modo più convenzionale (e meno puzzolente) – millhouse

+0

@millhouse Sì, sono assolutamente d'accordo che quel modo sembra il modo * giusto * per farlo. Mi chiedo però, c'è qualcosa di intrinsecamente sbagliato nel mio esempio in cui l'oggetto testato è una spia? – Matthew

risposta

3

Risponderò alla domanda in due parti. In primo luogo, sì è l'odore del codice di deridere o spiare la classe sotto test. Ciò non significa che non può essere fatto correttamente, ma che è a rischio incline e dovrebbe essere evitato quando possibile.

WRT il tuo esempio specifico, vorrei vedere come la spia potrebbe essere correttamente utilizzata, ma che sarebbe basata sull'affermazione che tu abbia altrove completamente testata l'unità getStringMetadata. Questo allora pone la domanda, se hai completamente testato l'unità getStringMetadata altrove, allora devi sapere come testarlo e quindi perché non testare getNumLines senza la spia.

Tutto ciò detto, millhouse è un buon punto, ma in entrambi i casi è necessario testare il codice costoso da qualche parte. Il suo suggerimento è molto utile per isolare il codice costoso e assicurarsi che sia necessario testarlo/esercitarlo una sola volta.

+0

Supponiamo di aver provato 'getStringMetadata' altrove. Vorrei ancora testare 'getNumLines' senza aver eseguito la costosa chiamata a' getStringMetadata' poiché so già che quel metodo si comporta come previsto. Il suggerimento di @ Millhouse è valido, e mi piace l'approccio di dividere 'getStringMetadata' in un'altra classe, ma la mia domanda originale rimane ancora. C'è qualcosa di * sbagliato * nel mio esempio.O forse un modo migliore per chiederglielo è, perché estrarre 'getStringMetadata' in un'altra classe e deridere quella dipendenza quando si prova' LineCounter' un approccio migliore? – Matthew

+0

Hai anche detto che spiare la classe sottoposta a test è a rischio. Puoi approfondire questo? – Matthew

+0

Hai chiesto se ha "odore di codice". Suggerisco che lo faccia. Non appena qualcuno lo vedrà, penserà "Aspetta, cosa sta succedendo qui". Ciò non significa che non possa essere fatto bene, solo che in generale dovrebbe essere evitato e quindi catturerà l'attenzione di un recensore. –

0

In questa situazione, è perfettamente legittimo arrestare il metodo chiamato dal metodo in prova. È anche l'unico modo in cui posso pensare di testarlo da solo. Semplicemente non vuoi estrarre un singolo metodo nella propria classe al solo scopo di testare.

Attenzione però agli effetti collaterali nel metodo di prova. Potrebbe non essere sufficiente per stubare il valore restituito, se il metodo stubbed ha effetti collaterali, allora devi anche eliminare gli effetti collaterali. Potrebbe anche essere una ragione contro di essa in alcune situazioni in cui gli effetti collaterali sono molto complessi, ma molto probabilmente sarebbe un'indicazione di un odore di codice nell'implementazione della classe sotto test stesso.

per rispondere alla tua domanda, trovo facile trovare motivi per , ma è difficile trovare motivi contro essa. È la tecnica che uso ogni giorno, mi aiuta a dividere la mia implementazione in piccoli metodi che sono testati individualmente in completo isolamento e non ho ancora riscontrato alcuna limitazione.