Le troppe affermazioni codificano l'odore?

19

Mi sono davvero innamorato del test unitario e del TDD - sono infetto da test.

Tuttavia, il test unitario viene normalmente utilizzato per i metodi pubblici. A volte però devo testare alcune assunzioni - asserzioni anche in metodi privati, perché alcune sono "pericolose" e il refactoring non può aiutare ulteriormente. (Lo so, i framework di test consentono di testare metodi privati).

Quindi è diventata mia abitudine che la prima e l'ultima riga di un metodo privato siano entrambe asserzioni.

Tuttavia, ho notato che tendo ad usare le asserzioni nei metodi pubblici (oltre che nel privato) solo "per essere sicuri". Potrebbe essere "testare la duplicazione" poiché le ipotesi del metodo pubblico sono testate dall'esterno dal framework di test delle unità?

Qualcuno potrebbe pensare a troppe asserzioni come un odore di codice?

    
posta Florents Tselai 19.06.2012 - 00:45
fonte

9 risposte

26

Queste asserzioni sono davvero utili per testare le tue ipotesi, ma servono anche un altro scopo veramente importante: la documentazione. Qualsiasi lettore di un metodo pubblico può leggere le asserzioni per determinare rapidamente le condizioni pre e post, senza dover guardare la suite di test. Per questo motivo, ti consiglio di mantenere tali asserzioni per ragioni di documentazione, piuttosto che per motivi di test. Tecnicamente stai duplicando le asserzioni, ma hanno due scopi diversi e sono molto utili in entrambi.

Mantenerli come affermazioni è meglio che usare semplicemente i commenti, perché controllano attivamente le ipotesi ogni volta che vengono eseguite.

    
risposta data 19.06.2012 - 00:59
fonte
15

Sembra che tu stia provando a fare Design-by-Contract a mano.

Fare DbC è una buona idea, ma dovresti almeno prendere in considerazione il passaggio a una lingua che la supporti in modo nativo (come Eiffel ) o almeno utilizzare un framework di contratto per la propria piattaforma (ad esempio Microsoft Code Contracts for .NET è abbastanza bello, probabilmente il più sofisticato quadro contrattuale là fuori, ancora più potente di Eiffel). In questo modo, puoi sfruttare meglio il potere dei contratti. Per esempio. usando un framework esistente, puoi far emergere i contratti nella tua documentazione o un IDE (ad esempio, i Contratti di codice per .NET sono mostrati in IntelliSense e, se hai VS Ultimate, puoi anche essere controllati staticamente da un dimostratore automatico di teoremi in fase di compilazione mentre tu tipo, così come molti framework di contratti Java hanno doclet JavaDoc che estrae automaticamente i contratti nella documentazione dell'API JavaDoc.

E anche se si scopre che nella tua situazione non c'è alternativa a farlo manualmente, ora sai almeno come si chiama, e puoi leggerlo a riguardo.

Quindi, in breve: se stai davvero facendo il DbC, anche se non lo sai, allora quelle affermazioni vanno perfettamente bene.

    
risposta data 19.06.2012 - 02:07
fonte
4

Ogni volta che non hai il pieno controllo sui parametri di input, è una buona idea testare in anticipo per errori semplici. Errore su null, ad esempio.

Questa non è una duplicazione dei test, poiché dovrebbero verificare che il codice non risponda correttamente dati parametri di input non validi, quindi documentare che .

Non penso che dovresti far valere i parametri di ritorno (a meno che tu non abbia esplicitamente un invariato che vuoi che il lettore capisca). Questo è anche il lavoro delle unittests.

Personalmente non mi piace l'istruzione assert in Java, dal momento che possono essere disattivati e quindi è una falsa sicurezza.

    
risposta data 25.06.2012 - 22:51
fonte
3

Penso che usare le asserzioni nei metodi pubblici sia ancora più importante, dal momento che non controlli gli input e potresti avere più probabilità di avere un'ipotesi errata.

Il test delle condizioni di input dovrebbe essere fatto in tutti i metodi pubblici e protetti. Se gli input vengono passati direttamente a un metodo privato, la verifica dei suoi input potrebbe essere ridondante.

Il test delle condizioni di output (ad es. stato dell'oggetto o quel valore restituito! = null) dovrebbe essere eseguito nei metodi interni (ad esempio metodi privati). Se gli output vengono passati direttamente da un metodo privato all'output di un metodo pubblico senza calcoli aggiuntivi o modifiche dello stato interno, la verifica delle condizioni di output del metodo pubblico può essere ridondante.

Sono d'accordo con Oleksi, tuttavia, che la ridondanza può aumentare la leggibilità e può anche aumentare la manutenibilità (se l'assegnazione diretta o la restituzione non è più il caso in futuro).

    
risposta data 19.06.2012 - 01:33
fonte
3

È difficile essere indipendenti dalla lingua in merito a questo problema, poiché i dettagli su come vengono implementate le asserzioni e la corretta gestione degli errori / delle eccezioni hanno un impatto sulla risposta. Ecco i miei $ 0.02, in base alla mia conoscenza di Java & C ++.

Le asserzioni nei metodi privati sono una buona cosa, assumendo che non esagerare e inserirle ovunque . Se li stai mettendo su metodi veramente semplici o controlli ripetutamente cose come campi immutabili, allora stai ingombrando inutilmente il codice.

Le asserzioni nei metodi pubblici sono generalmente meglio evitate. Dovresti comunque fare cose come controllare che il contratto del metodo non sia violato, ma se è così dovresti lanciare eccezioni opportunamente dattiloscritte con messaggi significativi, ripristinare lo stato, ove possibile, ecc. (Ciò che @rwong chiama "il pieno trattamento di gestione degli errori corretto ").

In generale, dovresti utilizzare solo le asserzioni per aiutare il tuo sviluppo / debug. Non puoi dare per scontato che chiunque usi la tua API avrà persino delle affermazioni abilitate quando usano il tuo codice. Anche se hanno qualche utilità nell'aiutare a documentare il codice, ci sono spesso modi migliori per documentare le stesse cose (ad esempio la documentazione del metodo, le eccezioni).

    
risposta data 19.06.2012 - 10:25
fonte
2

Aggiungendo alla lista di (per lo più) risposte eccezionali, un altro motivo per molte asserzioni, e la duplicazione, è che non sai come, quando o da chi la classe sarà modificata durante la sua vita. Se stai scrivendo buttare via il codice che sarà morto in un anno, non è una preoccupazione. Se stai scrivendo il codice che sarà in uso tra 20 anni, apparirà piuttosto diverso e un'ipotesi che hai fatto potrebbe non essere più valida. Il ragazzo che apporta quel cambiamento ti ringrazierà per gli asseriti.

Inoltre, non tutti i programmatori sono perfetti, anche in questo caso i test indicano che uno di questi "slittamenti" non si propagherà troppo lontano.

Non sottovalutare l'effetto che queste asserzioni (e altri controlli sulle ipotesi) avranno nel ridurre il costo della manutenzione.

    
risposta data 19.06.2012 - 07:30
fonte
0

Avere duplicati di asserzioni non è sbagliato di per sé, ma affermare che "solo per essere sicuri" non è il migliore. Si riduce davvero a ciò che esattamente ogni test sta cercando di realizzare. Asserisci solo ciò che è assolutamente necessario. Se un test utilizza Moq per verificare che venga invocato un metodo, non importa ciò che accade dopo che è stato richiamato quel metodo, il test riguarda solo assicurandosi che il metodo sia invocato.

Se ogni singola unità ha la stessa serie di asserzioni, tranne una o due, allora quando si refactoring un po 'tutti i test potrebbero fallire per lo stesso motivo, invece di mostrare il vero impatto del refactoring. Si può finire in una situazione in cui si eseguono tutti i test, tutti falliscono perché ogni test ha lo stesso asserzione, si risolve quell'errore, si eseguono di nuovo i test, falliscono per un altro motivo, si risolve quell'errore. Ripeti fino al completamento.

Considera anche la manutenzione del tuo progetto di test. Cosa succede quando aggiungi un nuovo parametro, o l'output è ottimizzato leggermente, dovresti tornare indietro attraverso una serie di test e modificare un assert o aggiungere un assert? E, a seconda del tuo codice, potresti finire per dover configurare molto di più solo per assicurarti che tutte le asserzioni passino.

Posso capire il fascino di voler includere affermazioni duplicate nel test unitario, ma è davvero eccessivo. Ho seguito lo stesso percorso con il mio primo progetto di test. Sono andato via da esso perché la manutenzione da solo mi stava facendo impazzire.

    
risposta data 19.06.2012 - 01:52
fonte
0

However, unit testing is used for public methods.

Se il tuo framework di testing consente di accedere, allora l'unit test dei metodi privati è anche una buona idea (IMO).

Could someone think of too many assertions as a code smell?

Sì. Le asserzioni vanno bene, ma il tuo primo obiettivo dovrebbe essere fare in modo che lo scenario non sia nemmeno possibile ; non solo che non succede.

    
risposta data 19.06.2012 - 03:05
fonte
-2

È una cattiva pratica.

Non dovresti testare metodi pubblici / privati. Dovresti testare la classe nel suo complesso. Assicurati solo di avere una buona copertura.

Se utilizzi il metodo (primitivo) di testare ciascun metodo separatamente come una regola empirica, sarà estremamente difficile ridefinire la tua classe in futuro. E questa è una delle cose migliori che TDD ti permette di fare.

Inoltre, è duplicazione. Inoltre, suggerisce che lo scrittore del codice non sapeva davvero cosa stava facendo. E la cosa divertente è, fai .

Alcune persone trattano i loro test con meno cura del loro codice di produzione. Non dovrebbero Se mai, la mia esperienza suggerisce di trattare anche i test con più attenzione. Ne vale la pena.

    
risposta data 19.06.2012 - 02:24
fonte

Leggi altre domande sui tag