TDD Verifica chiamata fittizia - è un anti-pattern?

10

Ho fatto TDD da un anno a questa parte, mi sento piuttosto bene, adoro i miei test suite e tutto il resto. Tuttavia, ho notato che ultimamente ho fatto un sacco di verifica delle chiamate simulate. Per esempio, avrei un servizio con un iniettore del repository - nel mio test unitario avrei passato una simulazione del repository e verificato che fosse chiamato all'interno del metodo che sto testando. Verificherei quindi se i risultati restituiti sono corretti (in un altro test). Questo sicuramente "sembra" sbagliato, dal momento che i miei test unitari sono ora molto accoppiati ai dettagli di implementazione. Ho sentito che dovresti testare il "comportamento", tuttavia in molte delle situazioni che ... emm - non è possibile? Se ad esempio hai un metodo void , di solito prova gli effetti collaterali. Voglio dire, è facile andare avanti e mostrare alcuni semplici code-kata in cui ciò può essere dimostrato, ma IMHO non riflette molto bene i programmi del mondo reale che scriviamo. Quello che sto facendo è sbagliato? Questo tipo di test è una sorta di anti-pattern? Apprezzerei la tua opinione su questo, sono ancora un po 'un principiante quando si tratta di TDD.

    
posta Dimitar Dimitrov 04.07.2013 - 07:02
fonte

3 risposte

6

Bene, dovresti provare a testare ingressi e uscite. Dovresti verificare il comportamento visibile esternamente. Le "promesse" o "contratti" che la tua classe fa.

Allo stesso tempo, a volte non esiste un modo migliore per testare un metodo piuttosto che fare ciò che hai detto.

Penso che renderà il test più fragile, quindi dovresti evitare test che si basano sui dettagli di implementazione, se puoi, ma non è un affare tutto o niente. A volte va bene, la cosa peggiore che succede è cambiare l'implementazione e aggiornare il test.

    
risposta data 04.07.2013 - 07:16
fonte
2

Lo scopo di un test è di limitare le possibili implementazioni produttive. Assicurati di mettere solo restrizioni all'implementazione di cui hai effettivamente bisogno. In genere questo è cosa dovrebbe fare il tuo programma, e non come lo fa.

Quindi se ad esempio il tuo servizio aggiunge qualcosa al repository, dovresti verificare che la nuova voce sia contenuta nel repository in seguito, e non che l'azione di aggiunta sia attivata.

Affinché funzioni, è necessario essere in grado di utilizzare l'implementazione del repository (testata altrove) nel test del servizio. Ho scoperto che utilizzare la vera implementazione di un collaboratore è generalmente un buon approccio, perché è davvero la migliore implementazione in giro.

"Quindi, ma se l'uso delle reali implementazioni nel test è costoso (ad esempio perché richiedono risorse complicate da configurare)? In questo caso, ho bisogno di usare i mock, giusto?"

In ogni caso, probabilmente vorrai un test di integrazione che verifica che le reali implementazioni funzionino insieme. Assicurati che questo test di integrazione sia tutto ciò che serve per testare il tuo servizio. In altre parole: se un servizio collega molti collaboratori (ed è quindi potenzialmente difficile da testare), assicurati che non contiene alcuna logica. Se lo fa, e avresti bisogno di più test (di integrazione), devi modificare la struttura del tuo codice, ad es. isolando la logica e quindi rendendola più testabile.

L'uso di mock in questo caso allevia il dolore di testare una parte di logica mal isolata, e quindi nasconde un problema di architettura . Quindi non usare i mock per testare codice mal strutturato, ma invece riparare la struttura.

    
risposta data 04.07.2013 - 10:41
fonte
2

I miei pensieri riguardano: "servizi aggregati".

La verifica delle chiamate lo farà, ma non fornirà molto valore. Stai solo controllando il tuo cablaggio.

Ci sono 3, non esclusivi, altri modi per questo:

  1. Estendi i test che hai per ogni singolo servizio in modo che controlli il comportamento di livello superiore. Ad esempio, se colpisci un database in memoria nella tua unit test del servizio, prendi un livello in modo da testare il servizio con un db effettivo. Il livello di servizio è più in alto nella struttura di astrazione, e così dovrebbe essere il tuo test.

  2. Utilizza la generazione del codice per creare il servizio direttamente dai servizi aggregati.

  3. Usa una sorta di riflesso o linguaggio dinamico per fare la stessa cosa. Ad esempio, in Java, potrebbe essere possibile utilizzare un'interfaccia groovy, che trasferisce direttamente la chiamata.

Ci sono probabilmente altri modi per farlo, ma solo il controllo dei cablaggi ha un ritorno molto basso e ti collegherà molto a questa implementazione.

    
risposta data 22.07.2013 - 22:59
fonte

Leggi altre domande sui tag