TDD Verifica chiamata simulata - è un anti-schema?


11

Faccio TDD da anni ormai, mi sento abbastanza bene, adoro le mie suite di test e tutto il resto. Tuttavia, ho notato che ultimamente ho fatto molte verifiche di chiamate simulate. Ad esempio, avrei un servizio a cui verrà iniettato un repository: nel mio test unit passerei una simulazione del repository e verificherei che fosse stato chiamato con il 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 associati ai dettagli di implementazione. Ho sentito che dovresti testare il "comportamento", tuttavia in molte situazioni che ... emm - impossibile? Se hai unvoidmetodo, ad esempio, di solito si verificano effetti collaterali. Voglio dire, è facile andare avanti e mostrare alcuni semplici code-kata dove questo può essere dimostrato, ma IMHO non si riflette molto bene sui programmi del mondo reale che scriviamo. Quello che sto facendo è sbagliato? Questo tipo di test è una specie di anti-pattern? Apprezzerei la tua opinione su questo, sono ancora un po 'un principiante quando si tratta di TDD.


2
Risposta breve: si. Ci sono domande molto interessanti su questo argomento già da qualche parte qui. I test unitari non dovrebbero essere fragili e dipendere fortemente dalla tua implementazione. Ecco perché sono previsti test di livello superiore (integrazione, ecc.). Qui: programmers.stackexchange.com/questions/198453/…
Kemoda

@Kemoda Ti sarei grato se mi collegassi a una discussione o altro materiale su questo, mi piacerebbe molto migliorare le mie tecniche.
Dimitar Dimitrov il

1
avete questo per esempio programmers.stackexchange.com/questions/198453/… troverò altri link in seguito
Kemoda

Risposte:


8

Bene, dovresti provare a testare input e output. Dovresti verificare il comportamento visibile esternamente. Le "promesse" o il "contratto" che la tua classe fa.

Allo stesso tempo, a volte non c'è modo migliore per testare un metodo che fare quello che hai detto.

Penso che renda il tuo test più fragile, quindi dovresti evitare i 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.


2

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

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

Affinché ciò funzioni, devi essere in grado di utilizzare l'implementazione del repository (testato altrove) nel test del servizio. Ho scoperto che l'utilizzo della vera implementazione di un collaboratore è generalmente un buon approccio, perché è davvero la migliore implementazione in circolazione.


"Quindi, e se l'utilizzo delle implementazioni reali nel test fosse costoso (ad es. Perché richiedono risorse complicate da impostare)? In questo caso devo usare le beffe, giusto?"

In ogni caso, probabilmente vorrai un test di integrazione che verifica che le implementazioni reali funzionino insieme. Assicurati che questo test di integrazione sia tutto ciò che è necessario per testare il tuo servizio. O in altre parole: se un servizio mette insieme molti collaboratori (ed è quindi potenzialmente difficile da testare), assicurarsi che non contenga alcuna logica. In tal caso, e avresti bisogno di più test (di integrazione), devi cambiare la struttura del tuo codice, ad esempio isolando la logica e quindi rendendola più testabile.

L'uso di simulazioni in questo caso allevia il dolore di testare un pezzo di logica mal isolata, e quindi nasconde un problema architettonico . Quindi non usare mock per testare codice mal strutturato, ma correggi invece la struttura.


1
Capisco quello che stai dicendo. Questo argomento è un po 'confuso su "quanti test sono troppi test", diciamo che ho un "servizio aggregato" che fondamentalmente è una facciata e "incolla" insieme un insieme di altri servizi / repository / componenti che tipo di test scrivi per questo? Tutto quello che riesco a pensare è la verifica delle chiamate. Spero di avere un senso.
Dimitar Dimitrov,

2

I miei pensieri sono: "servizi aggregati".

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

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

  1. Estendi i test che hai per ogni singolo servizio in modo che controlli il comportamento di livello superiore. Ad esempio, se si colpisce un database in memoria nei test unitari del servizio, salire di livello in modo da testare il servizio su un db effettivo. Il livello di servizio è più in alto nella struttura di astrazione, così come il test.

  2. Utilizzare la generazione di codice per creare il servizio direttamente dai servizi aggregati.

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

Probabilmente ci sono altri modi per farlo, ma solo il controllo del cablaggio ha un ritorno molto basso e ti collegherà a questa implementazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.