it-swarm.it

Cosa dovresti testare con unit test?

Sono appena uscito dal college e inizierò l'università da qualche parte la prossima settimana. Abbiamo visto unit test, ma non li abbiamo usati molto; e tutti ne parlano, quindi ho pensato che forse avrei dovuto fare un po '.

Il problema è che non lo so cosa da testare. Devo testare il caso comune? Il caso Edge? Come faccio a sapere se una funzione è adeguatamente coperta?

Ho sempre la terribile sensazione che mentre un test dimostrerà che una funzione funziona per un determinato caso, è assolutamente inutile provare che la funzione funziona, punto.

128
zneak

La mia filosofia personale è stata così:

  1. Prova il caso comune di tutto ciò che puoi. Questo ti dirà quando quel codice si romperà dopo aver apportato alcune modifiche (che è, a mio avviso, il principale vantaggio dei test di unità automatizzati).
  2. Prova i casi Edge di alcuni codici insolitamente complessi che pensi possano avere errori.
  3. Ogni volta che trovi un bug, scrivi un test case per coprirlo prima di risolverlo
  4. Aggiungi test Edge case a codice meno critico ogni volta che qualcuno ha il tempo di uccidere.
124
Fishtoaster

Tra la pletora di risposte finora nessuno ha toccato partizionamento di equivalenza e analisi del valore limite , considerazioni vitali nella risposta alla domanda attuale. Tutte le altre risposte, sebbene utili, sono qualitative ma è possibile - e preferibile - essere quantitative qui. @fishtoaster fornisce alcune linee guida concrete, che danno una sbirciatina sotto le copertine della quantificazione dei test, ma il partizionamento dell'equivalenza e l'analisi del valore limite ci consentono di fare meglio.

Nel partizionamento di equivalenza , dividi l'insieme di tutti i possibili input in gruppi in base ai risultati previsti. Qualsiasi input da un gruppo produrrà risultati equivalenti, quindi tali gruppi sono chiamati classi di equivalenza. (Nota che risultati equivalenti = no significano risultati identici.)

Ad esempio, si consideri un programma che dovrebbe trasformare caratteri minuscoli ASCII in caratteri maiuscoli. Altri caratteri dovrebbero subire una trasformazione di identità, cioè rimanere invariati. Ecco una possibile suddivisione in classi di equivalenza:

| # |  Equivalence class    | Input        | Output       | # test cases |
+------------------------------------------------------------------------+
| 1 | Lowercase letter      | a - z        | A - Z        | 26           |
| 2 | Uppercase letter      | A - Z        | A - Z        | 26           |
| 3 | Non-alphabetic chars  | [email protected]#,/"... | [email protected]#,/"... | 42           |
| 4 | Non-printable chars   | ^C,^S,TAB... | ^C,^S,TAB... | 34           |

L'ultima colonna riporta il numero di casi di test se li si elencano tutti. Tecnicamente, secondo la regola 1 di @ fishtoaster includeresti 52 casi di test, tutti quelli per le prime due file sopra riportati rientrano nel "caso comune". La regola 2 di @ fishtoaster aggiungerebbe anche alcune o tutte le righe 3 e 4 sopra. Ma con il test del partizionamento di equivalenza è sufficiente qualsiasi un caso di test in ogni classe di equivalenza. Se scegli "a" o "g" o "w" stai testando lo stesso percorso di codice. Pertanto, hai un totale di 4 casi di test anziché 52+.

L'analisi del valore limite raccomanda un leggero perfezionamento: in sostanza suggerisce che non tutti i membri di una classe di equivalenza sono, beh, equivalenti. Cioè, i valori ai confini dovrebbero essere considerati degni di un caso di prova a sé stanti. (Una semplice giustificazione per questo è il famigerato errore off-by-one !) Pertanto, per ogni classe di equivalenza potresti avere 3 input di test. Osservando il dominio di input sopra - e con una certa conoscenza di ASCII) potrei trovare questi input per i casi di test:

| # | Input                | # test cases |
| 1 | a, w, z              | 3            |
| 2 | A, E, Z              | 3            |
| 3 | 0, 5, 9, !, @, *, ~  | 7            |
| 4 | nul, esc, space, del | 4            |

(Non appena ottieni più di 3 valori limite che suggeriscono che potresti voler ripensare le delineazioni originali della classe di equivalenza, ma questo è stato abbastanza semplice da non tornare indietro per rivederle.) Pertanto, l'analisi del valore limite ci porta solo a 17 casi di test - con un'alta affidabilità di copertura completa - rispetto a 128 casi di test per eseguire test esaustivi. (Per non parlare del fatto che la combinatoria impone che test esaustivi siano semplicemente impossibili per qualsiasi applicazione nel mondo reale!)

68
Michael Sorens

Probabilmente la mia opinione non è troppo popolare. Ma ti suggerisco di essere economico con i test unitari. Se hai troppi test unitari puoi facilmente passare la metà del tuo tempo o più con il mantenimento dei test piuttosto che con la codifica effettiva.

Ti suggerisco di scrivere dei test per cose che hai un brutto istinto o cose molto cruciali e/o elementari. I test unitari IMHO non sostituiscono una buona ingegneria e codifica difensiva. Attualmente lavoro su un progetto che è più o meno inusuale. È davvero stabile ma è un dolore da refactoring. In effetti nessuno ha toccato questo codice in un anno e lo stack software su cui si basa ha 4 anni. Perché? Perché è ingombro di test unitari, per essere precisi: test unitari e test di integrazione automatizzati. (Hai mai sentito parlare di cetrioli e simili?) Ed ecco la parte migliore: questo (ancora) inutile pezzo di software è stato sviluppato da un'azienda i cui dipendenti sono pionieri nella scena dello sviluppo guidato dai test. : D

Quindi il mio suggerimento è:

  • Inizia a scrivere test dopo hai sviluppato lo scheletro di base, altrimenti il ​​refactoring può essere doloroso. Come sviluppatore che sviluppa per gli altri non ottieni mai i requisiti all'inizio.

  • Assicurarsi che i test unitari possano essere eseguiti rapidamente. Se hai dei test di integrazione (come il cetriolo), va bene se impiegano un po 'più di tempo. Ma i test di lunga durata non sono divertenti, credimi. (Le persone dimenticano tutti i motivi per cui il C++ è diventato meno popolare ...)

  • Lascia questa roba TDD agli esperti TDD.

  • E sì, a volte ti concentri sui casi Edge, a volte sui casi comuni, a seconda di dove ti aspetti l'imprevisto. Tuttavia, se ti aspetti sempre l'imprevisto, dovresti davvero ripensare il flusso di lavoro e la disciplina. ;-)

20
Philip

Se stai testando prima con Test Driven Development, la tua copertura sarà nel range del 90% o superiore, perché non aggiungerai funzionalità senza prima scrivere un test unitario fallito.

Se stai aggiungendo dei test dopo il fatto, allora non posso raccomandare abbastanza di ottenere una copia di Lavorare efficacemente con il codice legacy di Michael Feathers e dare un'occhiata ad alcuni dei tecniche per l'aggiunta di test al codice e modi di refactoring del codice per renderlo più testabile.

8
Paddyslacker

Se inizi a seguire le pratiche Test Driven Development , ti ordineranno guida attraverso il processo e sapendo cosa testare arriverà naturalmente. Alcuni punti da cui iniziare:

I test vengono prima di tutto

Mai e poi mai scrivere il codice prima di scrivere i test. Vedi Red-Green-Refactor-Repeat per una spiegazione.

Scrivi test di regressione

Ogni volta che riscontri un bug, scrivi un testcase e assicurati che fallisca . A meno che tu non riesca a riprodurre un bug attraverso un testcase fallito, non lo hai davvero trovato.

Red-Green-Refactor-Repeat

Rosso: Inizia scrivendo un test basilare per il comportamento che stai cercando di implementare. Pensa a questo passaggio come a scrivere un codice di esempio che utilizza la classe o la funzione su cui stai lavorando. Assicurati che compili/non abbia errori di sintassi e che fallisca . Questo dovrebbe essere ovvio: non hai scritto alcun codice, quindi deve fallire, giusto? La cosa importante da imparare qui è che se non vedi il test fallire almeno una volta, non puoi mai essere sicuro che se passa, lo fa a causa di qualcosa che hai fatto a causa di una ragione fasulla.

Verde: Scrivi il codice più semplice e stupido che fa effettivamente passare il test. Non cercare di essere intelligente. Anche se vedi che c'è un caso Edge evidente ma il test prende in considerazione, non farlo scrivi il codice per gestirlo (ma non dimenticare il caso Edge: ne avrai bisogno dopo). L'idea è che ogni pezzo di codice che scrivi, ogni if, ogni try: ... except: ... dovrebbe essere giustificato da un caso di prova. Il codice non deve essere elegante, veloce o ottimizzato. Vuoi solo che il test passi.

Refactor: pulisci il tuo codice, ottieni i nomi dei metodi giusti. Verifica se il test sta ancora superando. Ottimizzare. Eseguire di nuovo il test.

Ripeti: Ricordi il caso Edge che il test non ha coperto, giusto? Quindi, ora è il suo grande momento. Scrivi una testcase che copre quella situazione, guardala fallire, scrivi un po 'di codice, guardalo passare, refactor.

Test tuo codice

Stai lavorando su un pezzo di codice specifico, ed è esattamente quello che vuoi testare. Questo significa che non dovresti testare le funzioni della libreria, la libreria standard o il tuo compilatore. Inoltre, cerca di evitare di testare il "mondo". Ciò include: chiamare API Web esterne, alcuni elementi ad uso intensivo di database, ecc. Ogni volta che puoi provare a simularlo (crea un oggetto che segue la stessa interfaccia, ma restituisce dati statici predefiniti).

6
Ryszard Szopa

Per i test unitari, iniziare con i test che fanno ciò per cui è stato progettato. Questo dovrebbe essere il primo caso che scrivi. Se parte del disegno è "dovrebbe generare un'eccezione se passi nella spazzatura", prova anche questo dato che fa parte del disegno.

Inizia con quello. Man mano che acquisisci esperienza nel fare la maggior parte dei test di base, inizierai a capire se è sufficiente o meno e inizierai a vedere altri aspetti del tuo codice che necessitano di test.

3
Bryan Oakley

La risposta di borsa è "testare tutto ciò che potrebbe rompersi" .

Cosa c'è di troppo semplice per rompere? Campi di dati, accessori di proprietà cerebrali e simili spese generali simili. Qualcos'altro probabilmente implementa una parte identificabile di un requisito e può beneficiare del test.

Naturalmente, il tuo chilometraggio - e le pratiche del tuo ambiente di lavoro - possono variare.

0
Jeffrey Hantin