La scrittura manuale dei test unitari è dimostrata da un esempio?


9

Sappiamo che scrivere test JUnit dimostra un percorso particolare attraverso il tuo codice.

Uno dei miei collaboratori ha commentato:

La scrittura manuale dei test unitari è Prova per esempio .

Veniva dallo sfondo di Haskell che ha strumenti come Quickcheck e la capacità di ragionare sul comportamento del programma con i tipi .

La sua implicazione era che ci sono molte altre combinazioni di input che non sono state provate da questo metodo per le quali il tuo codice non viene testato.

La mia domanda è: scrivere manualmente test unitari Prova per esempio?


3
No, non scrivere / usare i test. Affermare che i test unitari dimostrano che non c'è nulla di sbagliato nel programma è Proof by Example (una generalizzazione inappropriata). I test non riguardano la dimostrazione matematica della correttezza del codice - i test sono, per loro natura, controlli sperimentali. È una rete di sicurezza che ti aiuta a creare confidenza raccontandoti qualcosa sul codice. Ma sei tu quello che deve scegliere una buona strategia per sondare il codice e tu sei quello che deve interpretare cosa significano quei dati.
Filip Milovanović,

Risposte:


10

Se si scelgono casualmente gli input per il test, suppongo che sia possibile che tu stia esercitando un errore logico Prove per esempio.

Ma buoni test unitari non lo fanno mai. Invece, si occupano di intervalli e casi limite.

Ad esempio, se si dovessero scrivere unit test per una funzione a valore assoluto che accetta un numero intero come input, non è necessario testare ogni possibile valore di input per provare che il codice funziona. Per ottenere un test completo, sono necessari solo cinque valori: -1, 0, 1 e i valori massimo e minimo per l'intero di input.

Questi cinque valori testano ogni possibile intervallo e caso limite della funzione. Non è necessario testare ogni altro possibile valore di input (ovvero ogni numero che può essere rappresentato dal tipo intero) per ottenere un livello di confidenza elevato che la funzione funzioni per tutti i valori di input.


11
Un tester di codice entra in un bar e ordina una birra. 5 birre. -1 birre, MAX_VALUE birre, un pollo. un null.
Neil,

2
I "5 valori" sono pura assurdità. Considera una funzione banale come int foo(int x) { return 1234/(x - 100); }. Si noti inoltre che (a seconda di ciò che si sta testando) potrebbe essere necessario assicurarsi che l'input non valido ("fuori portata") restituisca risultati corretti (ad esempio che `` find_thing (cosa) `restituisce correttamente un tipo di stato" non trovato " se la cosa non è stata trovata).
Brendan,

3
@Brendan: non c'è nulla di significativo nel fatto che siano cinque valori; capita solo che siano cinque valori nel mio esempio. Il tuo esempio ha un numero diverso di test perché stai testando una funzione diversa. Non sto dicendo che ogni funzione richiede esattamente cinque test; lo hai dedotto leggendo la mia risposta.
Robert Harvey,

1
Le librerie di test generativi sono generalmente più efficaci nel testare i casi limite di quanto tu sia. Se, ad esempio, si sta utilizzando carri invece di numeri interi, la libreria potrebbe anche controllare -Inf, Inf, NaN, 1e-100, -1e-100, -0, 2e200... preferirei non avere a che fare quelli tutto manualmente.
Hovercouch,

@Hovercouch: se ne conosci una buona, mi piacerebbe saperlo. Il migliore che abbia mai visto era Pex; era incredibilmente instabile, però. Ricorda, stiamo parlando di funzioni relativamente semplici qui. Le cose diventano più difficili quando hai a che fare con cose come la logica di business nella vita reale.
Robert Harvey,

8

Qualsiasi test software è come "Prova per esempio", non solo test unitari usando uno strumento come JUnit. E questa non è una nuova saggezza, c'è una citazione di Dijkstra del 1960, che dice sostanzialmente lo stesso:

"I test mostrano la presenza, non l'assenza di bug"

(basta sostituire le parole "spettacoli" con "prove"). Tuttavia, questo vale anche per gli strumenti che generano dati di test casuali. Il numero di possibili input per una funzione del mondo reale è in genere maggiore di ordini di grandezza rispetto al numero di casi di test che uno può produrre e verificare rispetto a un risultato atteso nell'età dell'universo, indipendentemente dal metodo di generazione di tali casi, quindi anche se si utilizza uno strumento generatore per produrre molti dati di test, non vi è alcuna garanzia di non perdere l'unico caso di test che avrebbe potuto rilevare un determinato bug.

I test casuali possono talvolta rivelare un bug che è stato trascurato dai casi di test creati manualmente. Ma in generale, è più efficiente elaborare con attenzione i test sulla funzione da testare e assicurarsi che si ottenga un codice completo e una copertura del ramo con il minor numero possibile di test. A volte è una strategia fattibile combinare test generati manualmente e casualmente. Inoltre, quando si utilizzano test casuali, è necessario avere cura di ottenere i risultati in modo riproducibile.

Quindi i test creati manualmente non sono in alcun modo peggiori dei test generati casualmente, spesso piuttosto il contrario.


1
Qualsiasi suite di test pratici che utilizza il controllo casuale avrà anche test unitari. (Tecnicamente, i test unitari sono solo un caso degenerato di test casuali.) La tua formulazione suggerisce che i test randomizzati sono difficili da realizzare o che è difficile combinare test randomizzati e test unitari. Questo non è in genere il caso. A mio avviso, uno dei maggiori vantaggi dei test randomizzati è che incoraggia fortemente la scrittura di test come proprietà del codice che si intende conservare sempre. Preferirei che queste proprietà fossero esplicitamente dichiarate (e verificate!) Piuttosto che inferirle alcune prove puntuali.
Derek Elkins lasciò SE il

@DerekElkins: "difficile" è IMHO il termine sbagliato. I test casuali richiedono un certo sforzo, e questo è uno sforzo che riduce il tempo disponibile per i test artigianali (e se hai persone che seguono solo slogan come quello menzionato nella domanda, probabilmente non eseguiranno affatto lavori manuali). Basta lanciare molti dati di test casuali su un pezzo di codice è solo metà del lavoro, si deve anche produrre i risultati previsti per ciascuno di quegli input di test. Per alcuni scenari, questo può essere fatto automaticamente. Per altri no.
Doc Brown,

Mentre ci sono sicuramente momenti in cui è necessario un po 'di pensiero per scegliere una buona distribuzione, questo di solito non è un grosso problema. Il tuo commento suggerisce che stai pensando a questo nel modo sbagliato. Le proprietà che scrivi per il controllo randomizzato sono le stesse proprietà che scriveresti per il controllo del modello o per le prove formali. In effetti, possono essere e sono stati usati per tutte queste cose allo stesso tempo. Non ci sono "risultati attesi" che devi anche produrre. Invece, devi semplicemente dichiarare una proprietà che dovrebbe sempre essere valida. Alcuni esempi: 1) spingendo qualcosa su una pila e ...
Derek Elkins lasciò SE il

... quindi fare scoppiare dovrebbe essere lo stesso di non fare nulla; 2) qualsiasi cliente con un saldo superiore a $ 10.000 dovrebbe ottenere l'alto tasso di interesse del saldo e solo allora; 3) la posizione dello sprite è sempre all'interno del riquadro di selezione dello schermo. Alcune proprietà potrebbero corrispondere a test puntuali, ad esempio "quando la bilancia è $ 0 dare l'avvertenza di bilancio zero". Le proprietà sono specifiche parziali con l'ideale di ottenere una specifica totale. Avere difficoltà a pensare a queste proprietà significa che non si è chiari su quale sia la specifica e spesso significa che si avrebbe difficoltà a elaborare buoni test unitari.
Derek Elkins lasciò SE il

0

Scrivere manualmente i test è "prova per esempio". Ma lo è anche QuickCheck, e in misura limitata i sistemi. Tutto ciò che non è una verifica formale diretta sarà limitato in ciò che ti dice sul tuo codice. Invece, devi pensare in termini di merito relativo degli approcci.

I test generativi, come QuickCheck, sono davvero ottimi per spazzare un ampio spazio di input. È anche molto meglio per affrontare i casi limite rispetto ai test manuali: le librerie di test generativi saranno più esperte con te di te. D'altra parte, ti parlano solo di invarianti, non di output specifici. Quindi per convalidare il tuo programma sta ottenendo i risultati corretti, hai ancora bisogno di alcuni test manuali per verificarlo, in effetti foo(bar) = baz.

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.