Domanda

Qualche tempo fa ho rovinato diversi test unitari quando li ho sottoposti e li ho rifattorizzati per renderli più DRY - l'intento di ogni test non era più chiaro. Sembra che ci sia un compromesso tra la leggibilità e la manutenibilità dei test. Se lascio codice duplicato nei test unitari, sono più leggibili, ma poi se cambio SUT , Dovrò rintracciare e cambiare ogni copia del codice duplicato.

Sei d'accordo che questo compromesso esiste? In tal caso, preferisci che i tuoi test siano leggibili o gestibili?

È stato utile?

Soluzione

Il codice duplicato è un odore nel codice di test unitario tanto quanto in un altro codice. Se si dispone di codice duplicato nei test, è più difficile riformattare il codice di implementazione perché è necessario aggiornare un numero sproporzionato di test. I test dovrebbero aiutarti a fare il refactor con sicurezza, piuttosto che essere un grosso onere che impedisce il tuo lavoro sul codice in fase di test.

Se la duplicazione è impostata sul dispositivo, considera di fare un uso maggiore del metodo setUp o di fornire più (o più flessibilità) Metodi di creazione .

Se la duplicazione è nel codice che manipola il SUT, allora chiediti perché più unità cosiddette & # 8220; & # 8221; i test stanno esercitando la stessa identica funzionalità.

Se la duplicazione si trova nelle asserzioni, forse hai bisogno di alcune Asserzioni personalizzate . Ad esempio, se più test hanno una serie di asserzioni come:

assertEqual('Joe', person.getFirstName())
assertEqual('Bloggs', person.getLastName())
assertEqual(23, person.getAge())

Quindi forse hai bisogno di un singolo metodo assertPersonEqual , in modo da poter scrivere assertPersonEqual (Person ('Joe', 'Bloggs', 23), person) . (O forse devi semplicemente sovraccaricare l'operatore di uguaglianza su Person .)

Come accennato, è importante che il codice di test sia leggibile. In particolare, è importante che l ' intento di un test sia chiaro. Trovo che se molti test sembrano quasi uguali (ad esempio, tre quarti delle linee sono uguali o praticamente uguali) è difficile individuare e riconoscere le differenze significative senza leggerle attentamente e confrontarle. Quindi trovo che il refactoring per rimuovere la duplicazione aiuta la leggibilità, perché ogni riga di ogni metodo di test è direttamente pertinente allo scopo del test. Questo è molto più utile per il lettore rispetto a una combinazione casuale di linee direttamente rilevanti e di linee che sono solo boilerplate.

Detto questo, a volte i test esercitano situazioni complesse simili ma comunque significativamente diverse, ed è difficile trovare un buon modo per ridurre la duplicazione. Usa il buon senso: se ritieni che i test siano leggibili e chiarisca il loro intento, e ti senti a tuo agio con forse la necessità di aggiornare più di un numero teoricamente minimo di test durante il refactoring del codice invocato dai test, quindi accetta l'imperfezione e muoviti a qualcosa di più produttivo. Puoi sempre tornare e riformattare i test in seguito, quando l'ispirazione colpisce!

Altri suggerimenti

La leggibilità è più importante per i test. Se un test fallisce, vuoi che il problema sia ovvio. Lo sviluppatore non dovrebbe dover superare un sacco di codice di test fortemente ponderato per determinare esattamente cosa non ha funzionato. Non vuoi che il tuo codice di test diventi così complesso che devi scrivere unit-test-test.

Tuttavia, l'eliminazione della duplicazione è di solito una buona cosa, purché non oscuri nulla ed eliminare la duplicazione nei test può portare a un'API migliore. Assicurati solo di non superare il punto di rendimenti decrescenti.

Il codice di implementazione e i test sono animali diversi e le regole di factoring si applicano in modo diverso a loro.

Il codice o la struttura duplicati sono sempre un odore nel codice di implementazione. Quando inizi a implementare il boilerplate, devi rivedere le tue astrazioni.

D'altra parte, il codice di test deve mantenere un livello di duplicazione. La duplicazione nel codice di test raggiunge due obiettivi:

  • Mantenimento dei test disaccoppiati. Un eccessivo accoppiamento del test può rendere difficile la modifica di un singolo test non riuscito che deve essere aggiornato poiché il contratto è stato modificato.
  • Mantenere i test significativi in ??modo isolato. Quando un singolo test fallisce, deve essere ragionevolmente semplice scoprire esattamente cosa sta testando.

Tendo a ignorare la banale duplicazione nel codice di prova purché ciascun metodo di prova rimanga più corto di circa 20 righe. Mi piace quando il ritmo di installazione-corsa-verifica è evidente nei metodi di prova.

Quando la duplicazione si insinua in " verifica " parte dei test, è spesso utile definire metodi di asserzione personalizzati. Naturalmente, questi metodi devono ancora testare una relazione chiaramente identificata che può essere resa evidente nel nome del metodo: assertPegFitsInHole - > buono, assertPegIsGood - > male.

Quando i metodi di test diventano lunghi e ripetitivi, a volte trovo utile definire modelli di test di riempimento che richiedono alcuni parametri. Quindi i metodi di prova effettivi vengono ridotti a una chiamata al metodo modello con i parametri appropriati.

Per quanto riguarda molte cose nella programmazione e nei test, non esiste una risposta chiara. Devi sviluppare un gusto e il modo migliore per farlo è fare errori.

Puoi ridurre la ripetizione usando diversi tipi di metodi di utilità di test .

Sono più tollerante nei confronti della ripetizione nel codice di prova che nel codice di produzione, ma a volte ne sono stato frustrato. Quando cambi il design di una classe e devi tornare indietro e modificare 10 diversi metodi di test che eseguono tutti gli stessi passaggi di configurazione, è frustrante.

Sono d'accordo. Il compromesso esiste ma è diverso in luoghi diversi.

Sono più propenso a refactificare il codice duplicato per l'impostazione dello stato. Ma meno probabilità di refactoring la parte del test che esercita effettivamente il codice. Detto questo, se l'esercizio del codice richiede sempre più righe di codice, allora potrei pensare che sia un odore e un refactoring del codice reale sotto test. E ciò migliorerà la leggibilità e la manutenibilità sia del codice che dei test.

Jay Fields ha coniato l'espressione che "DSLs dovrebbe essere DAMP, non DRY", dove DAMP significa frasi descrittive e significative . Penso che lo stesso valga anche per i test. Ovviamente, troppa duplicazione non è buona. Ma rimuovere la duplicazione a tutti i costi è ancora peggio. I test dovrebbero agire come specifiche rivelatrici di intenti. Se, ad esempio, specifichi la stessa funzione da diverse angolazioni, è prevedibile una certa quantità di duplicazione.

ADORO rspec per questo:

Ha 2 cose da aiutare -

  • gruppi di esempio condivisi per testare comportamenti comuni.
    puoi definire un set di test, quindi "includere" quello impostato nei tuoi test reali.

  • contesti nidificati.
    puoi essenzialmente avere un metodo 'setup' e 'teardown' per un sottoinsieme specifico dei tuoi test, non solo per tutti nella classe.

Prima che .NET / Java / altri framework di test adottino questi metodi, meglio è (o potresti usare IronRuby o JRuby per scrivere i tuoi test, che personalmente ritengo sia l'opzione migliore)

Non penso che ci sia una relazione tra codice più duplicato e leggibile. Penso che il tuo codice di test dovrebbe essere buono come il tuo altro codice. Il codice non ripetuto è più leggibile del codice duplicato se fatto bene.

Idealmente, i test unitari non dovrebbero cambiare molto una volta scritti, quindi mi spingerei verso la leggibilità.

Avere i test unitari il più discreti possibile aiuta anche a mantenere i test focalizzati sulla funzionalità specifica a cui si rivolgono.

Detto questo, tendo a tentare di riutilizzare alcune parti di codice che uso più volte, come il codice di installazione che è esattamente lo stesso in una serie di test.

Ritengo che il codice di test richieda un livello di ingegneria simile che verrebbe normalmente applicato al codice di produzione. Possono certamente esserci argomenti a favore della leggibilità e concordo sul fatto che sia importante.

Nella mia esperienza, tuttavia, trovo che i test ben ponderati siano più facili da leggere e comprendere. Se ci sono 5 test che sembrano tutti uguali tranne per una variabile che è cambiata e l'asserzione alla fine, può essere molto difficile trovare qual è quel singolo elemento diverso. Allo stesso modo, se viene considerato in modo tale che solo la variabile che sta cambiando sia visibile e l'asserzione, allora è facile capire immediatamente cosa sta facendo il test.

Trovare il giusto livello di astrazione quando i test possono essere difficili e penso che valga la pena farlo.

" li ha rifattorizzati per renderli più ASCIUTTI - l'intento di ogni test non era più chiaro "

Sembra che tu abbia avuto problemi a fare il refactoring. Sto solo indovinando, ma se finisce meno chiaro, ciò non significa che hai ancora molto lavoro da fare in modo da avere test ragionevolmente eleganti che sono perfettamente chiari?

Ecco perché i test sono una sottoclasse di UnitTest, quindi puoi progettare buone suite di test che siano corrette, facili da convalidare e chiare.

In passato avevamo strumenti di test che utilizzavano linguaggi di programmazione diversi. È stato difficile (o impossibile) progettare piacevoli, facili da lavorare con i test.

Hai la piena potenza di - qualunque sia la lingua che stai usando - Python, Java, C # - quindi usa bene quella lingua. È possibile ottenere un codice di prova di bell'aspetto chiaro e non troppo ridondante. Non ci sono compromessi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top