Domanda

Come programmatore, ho comprato tutto il cuore nella filosofia TDD e prendere lo sforzo di rendere ampi test di unità per qualsiasi codice non banale che scrivo. A volte questa strada può essere doloroso (cambiamenti comportamentali che causano cascata cambiamenti unit test multipli; elevate quantità di ponteggio necessario), ma nel complesso mi rifiuto di programmare senza test che posso correre dopo ogni cambiamento, e il mio codice è molto meno bacato come risultato.

Di recente, ho giocato con Haskell, ed è libreria di test residente, QuickCheck. In modo nettamente diverso da TDD, QuickCheck pone l'enfasi sul invarianti test del codice, cioè, alcune proprietà che contengono più di tutti (o sostanziali sottoinsiemi) di input. Un esempio veloce: un algoritmo di ordinamento stabile dovrebbe dare la stessa risposta se si corre due volte, dovrebbe avere aumentare la produzione, dovrebbe essere una permutazione dell'ingresso, ecc Quindi, QuickCheck genera una varietà di dati casuali per verificare questi invarianti.

A me sembra, almeno per le funzioni puri (cioè, le funzioni senza effetti collaterali - e se non beffardo correttamente è possibile convertire le funzioni sporchi in puri), che il test invariante potrebbe soppiantare unit testing come un rigoroso superset di tali capacità. Ciascuna prova unità è costituita da un ingresso ed un'uscita (in linguaggi di programmazione, il "output" non è solo il ritorno della funzione, ma anche qualsiasi stato cambiato, ma questo può essere incapsulato). Si potrebbe plausibilmente creato un generatore casuale di ingresso che è abbastanza buono per coprire tutti gli ingressi test di unità che si sarebbe creato manualmente (e poi alcuni, perché sarebbe genererebbe casi che non avreste pensato); se si trova un bug nel programma a causa di alcune condizioni al contorno, a migliorare il tuo generatore casuale di ingresso in modo da generare quel caso anche.

La sfida, allora, è se sia o non è possibile formulare invarianti utili per ogni problema. Direi che è: è molto più semplice una volta che hai una risposta per vedere se è corretto quanto lo è quello di calcolare la risposta, in primo luogo. Pensando invarianti aiuta anche a chiarire la specifica di un algoritmo complesso molto meglio di casi di test ad hoc, che favoriscono una sorta di caso per caso pensando al problema. Si potrebbe utilizzare una versione precedente del programma come un modello di implementazione, o una versione di un programma in un'altra lingua. Ecc Alla fine, si potrebbe coprire tutte le vostre ex test-casi, senza dover codificare in modo esplicito un ingresso o un'uscita.

ho io impazzita, o sono io a qualcosa?

È stato utile?

Soluzione

Un anno dopo, ora credo di avere una risposta a questa domanda: No In particolare, unit test sarà sempre necessario ed utile per test di regressione, in cui un test è attaccato ad un! bug report e continua a vivere nella base di codice per evitare il bug da sempre tornare.

Tuttavia, sospetto che qualsiasi test unità può essere sostituita con un test cui ingressi sono generati casualmente. Anche nel caso di codice di imperativo, il “input” è l'ordine delle istruzioni imperativi è necessario effettuare. Naturalmente, anche se non vale la pena di creare il generatore di dati casuali, e se non è possibile effettuare il generatore di dati casuali hanno il diritto di distribuzione è un'altra questione. Unit testing è semplicemente un caso degenere in cui il generatore casuale dà sempre lo stesso risultato.

Altri suggerimenti

Quello che hai tirato in ballo è un ottimo punto - quando applicato solo per la programmazione funzionale. Lei ha affermato un mezzo per realizzare tutto questo con il codice imperativo, ma anche toccato il motivo per cui non è fatto - non è particolarmente facile

.

Penso che questo sia il motivo non sostituirà unit testing: non va bene per il codice imperativo stessa facilità

.

In dubbio

Ho sentito parlare solo di (non utilizzato) questo tipo di test, ma vedo due problemi potenziali. Mi piacerebbe avere commenti su ciascuno di essi.

risultati fuorvianti

Ho sentito parlare di test come:

  • reverse(reverse(list)) deve essere uguale alla list
  • unzip(zip(data)) deve essere uguale alla data

Sarebbe bello sapere che questi valgono per una vasta gamma di ingressi. ma entrambi questi test passerebbero se le funzioni semplicemente restituiscono il loro contributo.

Mi sembra che ci si vuole verificare che, ad esempio, reverse([1 2 3]) uguale [3 2 1] di dimostrare un comportamento corretto in almeno un caso, quindi Aggiungi alcuni test con dati casuali.

complessità test

Un test invariante che descrive appieno la relazione tra l'ingresso e l'uscita potrebbe essere più complessa della funzione stessa. Se è complessa, potrebbe essere pieno di bug, ma non avere le prove per i test.

Un buon test di unità, al contrario, è troppo semplice avvitare o fraintendere come lettore. Solo un errore di battitura potrebbe creare un bug di "aspettarsi reverse([1 2 3]) alla parità [3 2 1]".

Quello che hai scritto nel tuo post originale, mi ha ricordato di questo problema, che è una questione aperta su ciò che l'invariante di ciclo è quello di dimostrare il ciclo corretto ...

In ogni modo, io non sono sicuro di quanto avete letto nelle specifiche formali, ma ci si sta muovendo verso il basso quella linea di pensiero. libro di David Gries è uno dei classici sull'argomento, non ho ancora imparato il concetto abbastanza bene per usarlo rapidamente nel mio giorno di programmazione al giorno. la solita risposta alla specifica formale è, la sua difficile e complicato, e solo vale la pena se si sta lavorando su sistemi critici di sicurezza. ma penso che ci sono tornati di tecniche busta simile a quello che quickcheck espone che può essere utilizzato.

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