È ragionevole non scrivere unit test perché tendono a essere commentati in seguito o perché i test di integrazione sono più preziosi?


35

Stavo discutendo dei test di unità / integrazione con un collega e ha presentato un caso interessante contro la scrittura di test di unità. Sono un grande sostenitore di test di unità (principalmente JUnit), ma sono interessato a sentire i commenti degli altri, mentre ha fatto alcuni punti interessanti.

Per riassumere i suoi punti:

  • Quando si verificano importanti modifiche al codice (nuova serie di POJO, refactoring delle applicazioni principali, ecc.), I test unitari tendono a essere commentati anziché rielaborati.
  • È meglio dedicare tempo ai test di integrazione relativi ai casi d'uso, che rendono i test di portata minore meno / per niente importanti.

Pensi su questo? Sono ancora pro-unit test (come lo vedo costantemente produrre codice migliorato), anche se i test di integrazione sembrano almeno altrettanto preziosi.


9
L'hai detto tu stesso: la scrittura di unit test produce un codice migliore, perché ti costringe a scrivere codice che è testabile. Il codice unit-testable ha molte proprietà molto importanti che ne migliorano la qualità complessiva.
Robert Harvey,

1
@Robert Harvey - Concordato - Sarei interessato a vedere un elenco di queste proprietà.
Jeff Levine,

18
Per quanto riguarda il punto pugno, in pratica stai dicendo che uno svantaggio dei test unitari è che non funzionano quando non li usi. Puoi dire la stessa cosa di qualsiasi altra pratica. Inoltre, il tuo uso della voce passiva sta sorvolando il fatto che qualcuno sta attivamente commentando i test unitari. Quindi sembra che tu non abbia buy-in dagli sviluppatori nel team, il che è un problema diverso.
Jacques B


Risposte:


62

Tendo a schierarmi con il tuo amico perché troppo spesso i test unitari mettono alla prova le cose sbagliate .

I test unitari non sono intrinsecamente negativi. Ma spesso testano i dettagli di implementazione piuttosto che il flusso di input / output. Si finisce con test completamente inutili quando questo accade. La mia regola è che un buon test unitario ti dice che hai appena rotto qualcosa; un cattivo test unitario ti dice semplicemente che hai appena cambiato qualcosa.

Un esempio al di sopra della mia testa è un test che è stato nascosto in WordPress qualche anno fa. La funzionalità testata ruotava attorno a filtri che si chiamavano l'un l'altro e i test stavano verificando che i callback sarebbero stati chiamati nell'ordine corretto. Invece di (a) eseguire la catena per verificare che i callback vengano chiamati nell'ordine previsto, i test si sono concentrati sulla (b) lettura di uno stato interno che probabilmente non avrebbe dovuto essere esposto all'inizio. Cambia gli interni e (b) diventa rosso; mentre (a) diventa rosso solo se le modifiche interne infrangono il risultato atteso mentre lo fanno. (b) è stato chiaramente un test inutile a mio avviso.

Se hai una classe che espone alcuni metodi al mondo esterno, la cosa corretta da testare a mio avviso sono solo questi ultimi . Se testate anche la logica interna, potreste finire per esporre la logica interna al mondo esterno, usando metodi di test contorti o con una litania di unit test che si interrompono invariabilmente ogni volta che volete cambiare qualcosa.

Detto questo, sarei sorpreso se il tuo amico è critico nei confronti dei test unitari di per sé come sembri suggerire. Piuttosto, vorrei dire che è pragmatico. Ossia, ha osservato che i test unitari che vengono scritti sono praticamente inutili nella pratica. Citando: "i test unitari tendono ad essere commentati piuttosto che rielaborati". Per me c'è un messaggio implicito lì dentro - se tendono ad avere bisogno di essere rielaborate è perché tendono a succhiare. Partendo dal presupposto, la seconda proposta segue: gli sviluppatori perderebbero meno tempo a scrivere codice che è più difficile sbagliare, ad esempio i test di integrazione.

In quanto tale, non si tratta di essere migliori o peggiori. È solo che uno è molto più facile sbagliare, e in effetti molto spesso nella pratica.


13
Questo, questo, cento volte questo. Questa è un'ottima cosa per lo sviluppo guidato dai test. Scrivere prima i test ti aiuterà a scrivere test che non testano i dettagli dell'implementazione ma il comportamento previsto che stavi cercando di implementare.
Wirrbel,

9
Penso che i fragili test unitari non siano una caratteristica intrinseca dei test unitari, ma invece di fraintendere a cosa servono i test unitari. Ciò di cui abbiamo bisogno qui è una migliore istruzione per gli sviluppatori. Se si eseguono i dettagli dell'implementazione del test unitario, lo si sta facendo male . Se rendi pubblici i metodi privati ​​"per testarli", stai sbagliando . Testa sempre l'interfaccia pubblica e il comportamento, che dovrebbe cambiare meno spesso dei dettagli di implementazione!
Andres F.

2
@DocBrown: No davvero. Quello che volevo dire è che spesso è fatto male che il collega di OP lo abbia corretto nella maggior parte dei casi - o almeno in tutti i casi in cui mi sono mai imbattuto in aziende ossessionate dai test unitari.
Denis de Bernardy,

3
@DenisdeBernardy: il mio punto è: tutto ciò che hai scritto è sicuramente corretto con molta saggezza dalla pratica, ma mi manca una conclusione che cosa significhi per il caso OP con il suo collega, nel contesto del suggerimento di "test di integrazione come alternativa migliore ".
Doc Brown,

5
Concordo pienamente con Doc Brown. In sostanza, la tua posizione sembra essere che i test unitari siano buoni, ma quando sono scritti male, diventano una seccatura. Pertanto, non sei affatto d'accordo con il collega di OP, ma semplicemente assicurati di scrivere effettivamente unit test prima di affermarne l'utilità. Penso che la tua risposta sia molto confusa poiché la prima frase afferma che sei d'accordo con il collega di OP quando non sembra essere il caso. Sembra più un rant su test unitari scritti male (che è un problema in pratica, ammetto), non un caso contro test unitari in generale.
Vincent Savard,

38

Quando si verificano importanti cambiamenti di codice, i test unitari tendono ad essere commentati piuttosto che rielaborati.

Con un gruppo indisciplinato di programmatori di cowboy che pensano che tutti i test che diventano "verdi" siano soddisfatti quando si commentano tutti i test esistenti: ovviamente. Rielaborare i test unitari richiede la stessa disciplina di scriverli in prima persona, quindi quello su cui devi lavorare qui è la "definizione del fatto" del tuo team.

È meglio dedicare tempo ai test di integrazione che coprono casi d'uso che rendono i test di portata minore meno / per niente importanti.

Questo non è né completamente sbagliato né completamente giusto. Lo sforzo per scrivere utili test di integrazione dipende molto dal tipo di software che stai scrivendo. Se hai un software in cui i test di integrazione possono essere scritti con la stessa facilità dei test unitari, funzionano ancora velocemente e ti offrono una buona copertura del tuo codice, quindi il tuo collega ha ragione. Ma per molti sistemi ho visto che questi tre punti non possono essere facilmente soddisfatti dai test di integrazione, ecco perché dovresti cercare di fare anche test unitari.


È così che mi sono sentito mentre ne discutevamo. Supponendo che sia possibile scrivere test di integrazione completi e pertinenti per un caso d'uso, sembra che il test unitario sia un punto controverso. (Anche se ora che scrivo questo, sto pensando a casi futuri imprevisti - come il nostro backend che viene trasformato in un servizio web per un'altra applicazione).
Jeff Levine,

+1 Per "devi lavorare sulla tua definizione di fatto". Ben detto!
Andres F.

14

Non so che accetto gli argomenti del tuo collega.

  1. Se i test unitari vengono commentati, è perché qualcuno è pigro. Non è colpa dei test unitari. E se inizi a usare lazy come scusa, puoi litigare con qualsiasi pratica che richiede un piccolo sforzo.
  2. Se lo stai facendo nel modo giusto, i test unitari non devono richiedere molto tempo. Non ti impediscono di fare lavori più importanti. Se siamo tutti d'accordo sul fatto che correggere il codice non funzionante è più importante di ogni altra cosa, un test unitario è dannatamente importante. È un modo semplice per testare le condizioni postali. Senza di essa, come fai a sapere di aver soddisfatto le garanzie delle condizioni postali? E se non l'hai fatto, il tuo codice è rotto.

Esistono altri argomenti più convincenti contro i test unitari. Beh, non esattamente contro di loro. Inizia con i danni di progettazione indotti dal test di DHH . È uno scrittore divertente, quindi leggilo. Cosa ne ho preso:

Se si apportano grandi modifiche alla progettazione per adattarsi ai test unitari, potrebbero ostacolare altri obiettivi nel progetto. Se possibile, chiedi a una persona imparziale quale approccio è più facile da seguire. Se il tuo codice altamente testabile è difficile da capire, potresti perdere tutti i vantaggi ottenuti dai test unitari. Il sovraccarico cognitivo di troppa astrazione ti rallenta e rende più facili gli errori che perdono. Non lo sconto.

Se vuoi diventare sfumato e filosofico, ci sono molte grandi risorse:


+1 per quell'articolo, potrebbe essere una risposta migliore al caso dei PO di tutto ciò che possiamo scrivere qui-
Doc Brown,

+1 per il punto numero 1 sulla pigrizia come scusa. Non posso sottolineare abbastanza quanto sia frustrante. Ci sono stato. Di recente, in realtà.
Greg Burghardt,

3
Il motivo 1 non è colpa dei test unitari, ma è ancora un motivo contrario ai test unitari.
user253751

Dopo aver letto l'articolo di DHH, assicurati di cercare i video in cui ha discusso l'argomento con Kent Beck e Martin Fowler (sono su YouTube) - molte persone sembrano ottenere una versione più estrema del suo punto di quanto intendesse, e lo affina un po 'in queste discussioni.
Jules,

6

Quando si verificano importanti modifiche al codice (nuova serie di POJO, refactoring delle applicazioni principali, ecc.), I test unitari tendono a essere commentati anziché rielaborati.

Non farlo. Se trovi qualcuno che lo fa, uccidilo e assicurati che non si riproduca, poiché i suoi figli potrebbero ereditare quel gene difettoso. La chiave per come la vedo mentre guardo i programmi televisivi CSI è di spargere un sacco di campioni di DNA fuorvianti ovunque. In questo modo inquini la scena del crimine con così tanto rumore che, data la natura costosa e che richiede tempo del test del DNA, la probabilità che te la fissino è astronomicamente bassa.


4

i test unitari tendono ad essere commentati piuttosto che rielaborati.

A quel punto il processo di revisione del codice dice "vai a correggere i test unitari" e questo non è più un problema :-) Se il tuo processo di revisione del codice non rileva questo tipo di grave difetto nel codice inviato, allora questo è il problema.


3

Quando si verificano importanti modifiche al codice (nuova serie di POJO, refactoring delle applicazioni principali, ecc.), I test unitari tendono a essere commentati anziché rielaborati.

Cerco sempre di mantenere separati il ​​refactoring e il cambio di funzionalità. Quando devo fare entrambe le cose, di solito commetto prima il refactoring.

Quando si effettua il refactoring senza modificare la funzionalità, si suppone che i test unitari esistenti contribuiscano a garantire che il refactoring non interrompa accidentalmente la funzionalità. Quindi per un tale impegno considererei la disabilitazione o la rimozione dei test unitari come un segnale di avvertimento importante. A qualsiasi sviluppatore che lo fa dovrebbe essere detto di non farlo quando il codice viene rivisto.

È possibile che i cambiamenti che non cambiano la funzionalità causino il fallimento dei test unitari a causa di test unitari difettosi. Se si capisce il codice che si sta modificando, la causa di tali guasti ai test unitari è di solito immediatamente ovvia e facile da risolvere.

Ad esempio, se una funzione accetta tre argomenti, un unit test che copre l'interazione tra i primi due argomenti per la funzione potrebbe non aver avuto cura di fornire un valore valido per il terzo argomento. Questo difetto nel test unitario può essere esposto da un refactoring del codice testato, ma è facile da risolvere se si capisce che cosa deve fare il codice e cosa sta testando il test unitario.

Quando si modificano le funzionalità esistenti, di solito sarà necessario modificare anche alcuni test unitari. In questo caso i test unitari aiutano a garantire che il codice cambi la funzionalità come previsto e che non abbia effetti collaterali indesiderati.

Quando si correggono i bug o si aggiungono nuove funzionalità, di solito è necessario aggiungere più unit test. Per quelli può essere utile eseguire prima i test unitari e successivamente la correzione dei bug o le nuove funzionalità. Ciò semplifica la verifica che i nuovi test unitari non siano stati superati con il codice precedente ma siano passati con il codice più recente. Questo approccio non è del tutto privo di inconvenienti, quindi esistono anche argomenti a favore del commit simultaneo di nuovi test unitari e aggiornamenti del codice.

È meglio dedicare tempo ai test di integrazione relativi ai casi d'uso, che rendono i test di portata minore meno / per niente importanti.

C'è qualche elemento di verità in questo. Se è possibile ottenere una copertura degli strati inferiori dello stack software con test mirati agli strati superiori dello stack software, i test potrebbero essere più utili durante il refactoring del codice.

Non credo che troverai un accordo sull'esatta distinzione tra un test unitario e un test di integrazione. E non mi preoccuperei se hai un caso di test che uno sviluppatore chiama un unit test e un altro chiama un test di integrazione, purché possano concordare che si tratta di un utile test case.


3

Vengono eseguiti test di integrazione per verificare se il sistema si comporta come dovrebbe, il che ha sempre valore commerciale. I test unitari non sempre lo fanno. I test unitari potrebbero testare il codice morto, ad esempio.

Esiste una filosofia di test che afferma che qualsiasi test che non ti fornisce informazioni è uno spreco. Quando un pezzo di codice non viene elaborato, i suoi test unitari diventano sempre (o quasi sempre) verdi, fornendo poche o nessuna informazione.

Ogni metodo di prova sarà incompleto. Anche se ottieni una copertura del 100% con alcune metriche, non stai ancora esaminando quasi tutti i possibili set di dati di input. Quindi, non pensare che i test unitari siano magici.

Ho visto spesso l'affermazione che avere test unitari migliora il tuo codice. A mio avviso, i miglioramenti apportati dai test unitari al codice stanno costringendo le persone che non sono abituate a suddividere il proprio codice in piccoli pezzi ben ponderati. Se ti abitui a progettare il tuo codice in piccoli pezzi ben ponderati normalmente, potresti scoprire che non hai bisogno di unit test per costringerti a farlo più.

A seconda di quanto pesantemente test unitario e quanto è grande il tuo programma, potresti finire con un numero enorme di test unitari. In tal caso, i grandi cambiamenti diventano più difficili. Se una grande modifica provoca molti test unitari falliti, puoi rifiutare di apportare la modifica, fare molto lavoro per sistemare molti test o buttare via i test. Nessuna delle scelte è l'ideale.

I test unitari sono uno strumento nella casella degli strumenti. Sono lì per servirti, non viceversa. Assicurati di uscire da loro almeno quanto hai messo.


3

I test unitari non sono sicuramente il proiettile d'argento che alcuni sostenitori affermano di essere. Ma in generale hanno un rapporto costi / benefici molto positivo. Non renderanno il tuo codice "migliore", forse più modulare. "migliore" ha molti più fattori di ciò che implica la "testabilità". Ma assicureranno che funziona in tutti i casi e gli usi a cui hai pensato, ed elimineranno la maggior parte dei tuoi bug (probabilmente i più banali).

Il più grande vantaggio dei test unitari è che rendono il codice più flessibile e resiliente alle modifiche. Puoi refactoring o aggiungere funzionalità ed essere abbastanza sicuro di non aver rotto nulla. Questo da solo li rende utili per la maggior parte dei progetti.

Facendo riferimento ai punti sollevati dal tuo amico:

  1. Se l'aggiunta di POJO interrompe i test unitari, probabilmente sono scritti molto male. I POJO sono di solito la lingua che stai usando per parlare del tuo dominio e i problemi che stai risolvendo, cambiandoli ovviamente significano la tua intera logica aziendale, e probabilmente il codice di presentazione deve cambiare. Non puoi aspettarti che cosa assicuri che quelle parti del tuo programma non cambino ...

  2. Lamentarsi che i test unitari siano negativi, perché le persone li commentano, è come lamentarsi che le cinture di sicurezza siano cattive perché le persone non le indossano. Se uno commenta test del codice e rompe qualcosa, dovrebbe finire in una conversazione molto seria e spiacevole con il suo capo ...

  3. I test di integrazione sono di gran lunga superiori ai test unitari. nessuna domanda. soprattutto se stai testando un prodotto. Ma scrivere buoni test di integrazione completi, che ti daranno lo stesso valore, la copertura e la certezza della correttezza che ti danno i test unitari, è incredibilmente difficile.


2

Posso solo pensare a un argomento contro i test unitari ed è piuttosto debole.

I test unitari mostrano la maggior parte del loro valore quando si refactoring o si modifica il codice in una fase successiva. Noti una notevole riduzione del tempo necessario per aggiungere funzionalità e assicurarti di non aver rotto nulla.

Tuttavia: in primo luogo c'è un costo (piccolo) per la scrittura dei test.

Pertanto: se un progetto è abbastanza breve e non richiede manutenzione / aggiornamenti continui, è possibile che il costo della scrittura dei test superi i loro benefici.


+1 per aver tentato di rispondere seriamente alla domanda che è stata posta.
RubberDuck,

2
Il costo del test unitario non è definitivamente piccolo. I buoni test unitari di solito richiedono più tempo per scrivere rispetto a ciò che testano. Il più delle volte i benefici superano il costo.
AK_12

1
solo in teoria - quando si esegue il refactoring, si finisce con un enorme costo per aggiornare anche i test unitari, poiché sono quasi sempre troppo granulari. Se stavi parlando di avere una buona suite di test di integrazione, allora avresti ragione.
gbjbaanb,

Se un progetto è abbastanza breve e non richiede manutenzione / aggiornamenti continui - la maggior parte dei sistemi legacy ha iniziato come progetto breve senza test - e finisce con l'assunzione di più sviluppatori per mantenere un sistema cresciuto senza test
Fabio
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.