Test Internals con TDD?

3

Sono nuovo di TDD e relativamente nuovo allo sviluppo di software in generale (ad es. 4 anni di esperienza), ma sto cercando di imparare.

Ho giocato con TDD ma mi sono imbattuto in quello che so realizzare è un problema comune. Ho testato sia i contratti API di livello superiore, a volte con test di integrazione quando necessario, sia i test di unità dipendenti dall'implementazione di livello inferiore.

Di recente ho visto un colloquio di Ian Cooper che suggerisce che non dovremmo testare gli interni, solo ciò che è inteso effettiva funzionalità del programma (ad esempio, qualunque sia il contratto del programma). Nota che sto parafrasando qui, e potrebbe aver perso il messaggio.

Per me è logico che sarebbe utile permettere il refactoring senza preoccuparsi di test che rompono gli interni, ma allo stesso tempo sembra strano non testare molte delle funzioni interne (private o non) che scrivo per aiutami a raggiungere la più grande funzionalità a portata di mano. Inoltre, a volte se provo a scrivere test che testano solo a un livello di contratto elevato, i test diventano troppo grandi / complessi.

Inoltre, questo non contraddice la spinta principale per tonnellate di test unitari e che, in generale, più unità testano meglio perché ci aiutano a identificare il problema specifico quando falliscono piuttosto che essere omnibus?

Come faccio a bilanciarlo? È solo un atto di equilibrio che imparerò nel tempo attraverso l'esperienza?

    
posta Adam Thompson 08.03.2018 - 04:49
fonte

2 risposte

5

Also doesn't this contradict the major push for tons of unit tests and that in general the more unit tests the better because they help us identify the specific problem when they fail rather than being omnibus?

In un certo senso sì, ma non è necessariamente una brutta cosa.

Uno dei principali vantaggi di TDD è che non solo ti aiuta a definire i tuoi contratti ma anche ciò che è significativo come "unità".

Scrivi un test per una particolare funzionalità a un determinato livello, coprendo quello che ritieni sia un comportamento significativo.

Se superi i tuoi test - come nel mantra di cui sopra - non solo finisci con test che non descrivono nulla di significativo, ma finisci con il codice che è molto difficile da refactoring e riorganizzare perché ogni un piccolo cambiamento nel codice di produzione porta a una svolta per i test.

Quindi, lascia che gli interni siano gli interni - questo potrebbe essere un metodo, una classe o un gruppo di classi coesive. Come si definisce quale? Non lo fai. Trova dove ti senti è un punto di partenza comodo e scrivi un test che delinea la prima cosa che vuoi fare.

Quindi vai con il flusso.

Man mano che un'unità cresce, potresti trovare bisogno di aggiungere qualche astrazione o riorganizzare i componenti interni dell'unità per estendere il comportamento per soddisfare alcuni nuovi test. Bene - vai avanti. Aggiungi alcune classi, estrai alcune interfacce. Saprai se hai rotto qualcosa perché i tuoi test sono ancora verdi.

E se hai un problema che è più granulare (per esempio il test di livello più alto diventa rosso in certe condizioni a causa di un caso limite in un componente di livello inferiore), puoi Cerchiamo di andare avanti e scrivere un test più granulare: estendere il test a ciò che ti interessa come implementatore.

La cosa da tenere a mente è che qualsiasi punto di ingresso per un dispositivo di prova è un confine difficile che sarà difficile da cambiare . Tutto ciò che non è un confine rigido è banale da cambiare.

Ricorda che l'obiettivo di TDD non è quello di produrre una suite di test - questo è un buon effetto collaterale. L'obiettivo di TDD è quello di aiutarti a scrivere codice funzionante.

Per inciso, questo tipo di approccio è molto utile con il principio "tell, do not ask" - si passa il meccanismo di output in "unità" al livello più alto e si asserisce tutto contro un'implementazione di output fittizio :

VendingMachine vendingMachine = new VendingMachine();

vendingMachine.insertCoins(5);
vendingMachine.displayTo(fakeDisplay);

assertThat(fakeDisplay.toString()).isEqualTo("5");

Man mano che aggiungi comportamenti più complessi, interagisci ancora con l'interfaccia VendingMachine con i tuoi test e asserisci contro lo schermo ma, internamente, potresti avere più composizione in corso - il display potrebbe essere passato a sottocomponenti più specializzati ecc. ecc.

    
risposta data 08.03.2018 - 09:49
fonte
2

Se gli interni della tua classe sono così complessi da richiedere test approfonditi, la tua classe sta violando l'SRP (facendo troppo). Rifattalo in un client di una nuova classe di utilità esterna e verifica quella classe di utilità separatamente.

    
risposta data 08.03.2018 - 08:18
fonte