Ci sono troppe affermazioni sul codice?


19

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

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

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

Tuttavia, ho notato che tendo a usare le asserzioni nei metodi pubblici (così come nel privato) solo "per essere sicuri". Potrebbe trattarsi di "test di duplicazione" poiché i presupposti del metodo pubblico sono testati dall'esterno dal framework di unit testing?

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


1
queste affermazioni sono attivate o disattivate in fase di rilascio?
jk.

Lavoro principalmente con Java, dove le asserzioni devono essere abilitate manualmente.
Florents Tselai,

Ho appena trovato questo "Assert aumenta la produttività rintracciando i bug" programmers.stackexchange.com/a/16317/32342
Florents Tselai,

come definisci "troppi"?
Hayylem,

@haylem Ci sono "troppe" affermazioni se un altro programmatore legge il codice e dice "Sono stanco di queste asserzioni".
Florents Tselai,

Risposte:


26

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

Mantenerli come asserzioni è meglio che semplicemente usare i commenti, perché controllano attivamente i presupposti ogni volta che vengono eseguiti.


2
Ottimo punto!
Florents Tselai,

1
Le asserzioni sono documentazione, ma ciò non rende legittimo duplicarle. Al contrario, solleva persino sospetti sulla qualità dei test se sembra che le stesse affermazioni siano necessarie più di una volta.
Yam Marcovic,

1
@YamMarcovic Penso che questo sia accettabile perché i due pezzi di codice "duplicati" servono a due scopi diversi e distinti. Penso che sia utile averli in entrambe le posizioni, nonostante la duplicazione. Avere le asserzioni nel metodo è prezioso per la documentazione e sono ovviamente necessarie per i test.
Oleksi,

2
Le asserzioni nel codice per me sono complementari alle asserzioni dei test unitari. Non avrai una copertura dei test al 100% e le asserzioni nel codice ti aiuteranno a ridurre più rapidamente i test unitari falliti.
sebastiangeiger,

15

Sembra che tu stia provando a fare il Design per contratto a mano.

Fare DbC è una buona idea, ma dovresti almeno considerare di passare a una lingua che lo supporta in modo nativo (come Eiffel ) o almeno utilizzare un framework di contratto per la tua piattaforma (ad esempio Contratti di codice Microsoft per .NETè piuttosto bello, probabilmente il quadro di contratto più sofisticato là fuori, anche più potente di Eiffel). In questo modo, puoi sfruttare meglio il potere dei contratti. Ad esempio, utilizzando un framework esistente, è possibile presentare i contratti nella documentazione o in un IDE (ad esempio, i Contratti di codice per .NET sono mostrati in IntelliSense e, se si dispone di VS Ultimate, può anche essere verificato staticamente da un proore teorema automatico al momento della compilazione mentre si digita; allo stesso modo molti framework di contratto Java hanno doclet JavaDoc che estrarranno automaticamente i contratti nella documentazione dell'API JavaDoc).

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

Quindi, in breve: se stai effettivamente facendo DbC, anche se non lo conosci, quelle affermazioni vanno benissimo.


4

Ogni volta che non si ha il controllo completo sui parametri di input, è una buona idea testare in anticipo eventuali errori. Errore su null, ad esempio.

Questa non è la duplicazione dei vostri test, come dovrebbero verificare che il codice non appropriato dato parametri di input cattivi, e quindi documentare che .

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

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


1
+1 Per "Non mi piace l'affermazione assert in Java, poiché possono essere disattivati ​​e quindi è una falsa sicurezza".
Florents Tselai,

3

Penso che l'uso delle asserzioni nei metodi pubblici sia ancora più importante, dal momento che non si controllano gli input e potrebbe essere più probabile che un'ipotesi venga infranta.

Il test delle condizioni di input deve essere eseguito in tutti i metodi pubblici e protetti. Se gli input vengono passati direttamente a un metodo privato, la verifica dei relativi input può essere ridondante.

Il test delle condizioni di output (ad es. Stato dell'oggetto o quel valore di ritorno! = Null) deve essere eseguito nei metodi interni (ad es. 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).


4
Se una condizione di errore o una condizione argomento non valida può essere causata dall'utente (chiamante) dell'interfaccia pubblica, questo dovrebbe ricevere il trattamento completo di gestione degli errori. Ciò significa formattare un messaggio di errore significativo per l'utente finale, accompagnato da codice di errore, registrazione e tentativi di ripristinare i dati o ripristinare uno stato sano. Sostituire la corretta gestione degli errori con asserzioni renderà il codice meno robusto e più difficile da risolvere.
rwong,

Le asserzioni potrebbero (e in molti casi dovrebbero) includere la registrazione e il lancio di eccezioni (o codici di errore in C--).
Danny Varod,

3

È difficile essere indipendenti dal linguaggio riguardo a questo problema, poiché i dettagli su come vengono implementate le asserzioni e la gestione "corretta" di errori / eccezioni influiscono sulla risposta. Ecco i miei $ 0,02, in base alla mia conoscenza di Java e C ++.

Le asserzioni nei metodi privati ​​sono una buona cosa, supponendo che non esageriate e le mettiate ovunque . Se li stai mettendo su metodi davvero semplici o controlli ripetutamente cose come campi immutabili, allora stai ingombrando inutilmente il codice.

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

In generale, dovresti usare solo le asserzioni per aiutare il tuo sviluppo / debugging. Non puoi presumere che chiunque utilizzi la tua API disponga anche di asserzioni abilitate quando usano il tuo codice. Sebbene possano essere utili per documentare il codice, ci sono spesso modi migliori per documentare le stesse cose (ovvero la documentazione del metodo, le eccezioni).


2

Aggiungendo all'elenco di risposte (per lo più) eccezionali, un'altra ragione per molte affermazioni e duplicazioni, è che non sai come, quando o da chi la classe verrà modificata nel corso della sua vita. Se stai scrivendo il codice di eliminazione che sarà morto in un anno, non è un problema. Se stai scrivendo un codice che sarà in uso tra 20+ anni, sembrerà piuttosto diverso - e un'ipotesi che hai fatto potrebbe non essere più valida. Il ragazzo che apporta quel cambiamento ti ringrazierà per le affermazioni.

Inoltre, non tutti i programmatori sono perfetti - anche in questo caso, le asserzioni indicano che uno di quegli "errori" non si propagherà troppo.

Non sottovalutare l'effetto che queste asserzioni (e altri controlli sulle ipotesi) avranno nel ridurre i costi di manutenzione.


0

Avere affermazioni duplicate non è di per sé sbagliato, ma avere le affermazioni "solo per essere sicuri" non è il massimo. Si riduce davvero a ciò che esattamente ogni test sta cercando di realizzare. Afferma solo ciò che è assolutamente necessario. Se un test usa Moq per verificare che un metodo sia invocato, non importa cosa succede dopo che quel metodo è stato invocato, il test si preoccupa solo di assicurarsi che il metodo sia invocato.

Se ogni singolo test unitario ha lo stesso insieme di asserzioni, tranne uno o due, allora quando si refactera un po 'tutti i test potrebbero fallire per lo stesso identico motivo, invece di mostrarti il ​​vero impatto del refactor. Potresti finire in una situazione in cui esegui tutti i test, tutti falliscono perché ogni test ha le stesse affermazioni, correggi quell'errore, esegui di nuovo i test, falliscono per un altro motivo, risolvi quell'errore. Ripeti fino al termine.

Considera anche la manutenzione del tuo progetto di test. Cosa succede quando aggiungi un nuovo parametro, o se l'output viene modificato di tanto in tanto, dovresti tornare attraverso una serie di test e cambiare un'asserzione o aggiungere un'asserzione? E, a seconda del tuo codice, potresti finire per dover impostare molto di più solo per assicurarti che tutti gli asserti passino.

Riesco a 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. Me ne sono andato perché la sola manutenzione mi stava facendo impazzire.


0

Tuttavia, il test unitario viene utilizzato per metodi pubblici.

Se il framework di test consente l'accesso, allora anche i metodi privati ​​di unit test sono una buona idea (IMO).

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

Lo voglio. Le affermazioni vanno bene, ma il tuo primo obiettivo dovrebbe essere quello di rendere lo scenario impossibile ; non solo che non succede.


-2

È una cattiva pratica.

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

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

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

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


-1 Il test unitario di una classe nel suo insieme può essere pericoloso poiché si finisce per testare più di una cosa per test. Questo rende i test molto fragili. Dovresti testare un comportamento alla volta, che spesso corrisponde al test di un solo metodo per test.
Oleksi,

Anche per quanto riguarda "suggerisce che lo scrittore del codice non sapeva davvero cosa stesse facendo [...] tu." I futuri sviluppatori potrebbero non essere così chiari su ciò che fa un particolare metodo. In questo caso, avere condizioni pre e post sotto forma di asserzioni può essere prezioso per comprendere un pezzo di codice.
Oleksi,

2
@Oleksi Un buon test riguarda gli scenari di utilizzo validi, non l'affermazione di ogni minimo dettaglio irrilevante. Le affermazioni ridondanti sono in effetti un odore di codice perché l'intento oscuro e sono solo un altro cattivo esempio di programmazione difensiva.
Yam Marcovic,
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.