Che cosa dovresti testare con i test unitari?

110

Sono appena uscito dal college e ho iniziato l'università da qualche parte la prossima settimana. Abbiamo visto test unitari, ma non li abbiamo usati molto; e tutti parlano di loro, quindi ho pensato che forse dovrei fare un po '.

Il problema è che non so cosa testare. Dovrei testare il caso comune? Il caso limite? Come faccio a sapere se una funzione è adeguatamente coperta?

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

    
posta zneak 03.09.2010 - 18:28
fonte

7 risposte

106

La mia filosofia personale è stata quindi la seguente:

  1. Prova il caso comune di tutto ciò che puoi. Questo ti dirà quando il codice si interromperà dopo aver apportato qualche cambiamento (che è, a mio avviso, il più grande vantaggio del test automatico delle unità).
  2. Prova i casi limite di un codice insolitamente complesso che ritieni possa avere errori.
  3. Ogni volta che trovi un bug, scrivi un test case per coprirlo prima di ripararlo
  4. Aggiungi test del caso limite a un codice meno critico ogni volta che qualcuno ha il tempo di uccidere.
risposta data 03.09.2010 - 18:47
fonte
58

Tra la moltitudine di risposte, quindi nessuno ha toccato partizionamento di equivalenza e analisi del valore al contorno , considerazioni fondamentali nella risposta alla domanda in questione. Tutte le altre risposte, sebbene utili, sono qualitative ma è possibile - e preferibile - essere quantitative qui. @fishtoaster fornisce alcune linee guida concrete, semplicemente sbirciando sotto le copertine della quantificazione del test, ma l'analisi del partizionamento dell'equivalenza e del valore al contorno ci consente di fare meglio.

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

Come semplice esempio, considera un programma che dovrebbe trasformare i caratteri ASCII minuscoli in caratteri maiuscoli. Altri personaggi dovrebbero subire una trasformazione dell'identità, ovvero 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  | 0-9!@#,/"... | 0-9!@#,/"... | 42           |
| 4 | Non-printable chars   | ^C,^S,TAB... | ^C,^S,TAB... | 34           |

L'ultima colonna riporta il numero di casi di test se si elencano tutti. Tecnicamente, secondo la regola 1 di fishtoaster, dovresti includere 52 casi di test - tutti quelli per le prime due righe di cui sopra rientrano nel "caso comune". @ la regola 2 di Fishtoaster aggiungerebbe un po 'o tutto dalle righe 3 e 4 sopra pure. Ma con il partizionamento dell'equivalenza è sufficiente testare ogni caso di test one 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 test case invece di 52 +.

Analisi del valore al contorno raccomanda un leggero raffinamento: essenzialmente suggerisce che non tutti i membri di una classe di equivalenza sono, beh, equivalenti. Vale a dire, i valori ai limiti dovrebbero anche essere considerati degni di un caso di prova a pieno titolo. (Una semplice giustificazione per questo è l'infame errore !) Quindi, per ogni equivalenza classe si potrebbe avere 3 ingressi di prova. Osservando il dominio di input sopra - e con una certa conoscenza dei valori ASCII - potrei trovare questi input del test case:

| # | 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 alle tue delineazioni di classe di equivalenza originali, ma questo era abbastanza semplice da non tornare indietro per revisionarle.) Quindi, l'analisi del valore al contorno ci porta fino 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 il combinatorics impone che test esaurienti siano semplicemente irrealizzabili per qualsiasi applicazione del mondo reale!)

    
risposta data 21.11.2013 - 23:32
fonte
17

Probabilmente la mia opinione non è troppo popolare. Ma ti suggerisco di essere economico con i test unitari. Se disponi di troppi test di unità, puoi facilmente passare metà del tuo tempo o più con la manutenzione dei test anziché con la codifica effettiva.

Ti suggerisco di scrivere test per cose che hai un brutto presentimento nel tuo intestino o cose che sono molto cruciali e / o elementari. I test unitari IMHO non sostituiscono la buona ingegneria e la codifica difensiva. Attualmente lavoro su un progetto che è più o meno insolubile. È veramente stabile ma un dolore da refactoring. In effetti nessuno ha toccato questo codice in un anno e lo stack di 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) pezzo di software inutilizzabile è stato sviluppato da un'azienda i cui dipendenti sono pionieri nella scena dello sviluppo test-driven. : 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 hai mai i requisiti all'inizio.

  • Assicurati che i test dell'unità possano essere eseguiti rapidamente. Se hai test di integrazione (come cetriolo) va bene se impiegano più tempo. Ma i test a lungo termine non sono divertenti, credimi. (Le persone dimenticano tutti i motivi per cui il C ++ è diventato meno popolare ...)

  • Lascia questo materiale TDD agli esperti di TDD.

  • E sì, a volte ti concentri sui casi limite, a volte sui casi comuni, a seconda di dove ti aspetti l'imprevisto. Anche se ti aspetti sempre l'inaspettato, dovresti davvero ripensare al tuo flusso di lavoro e alla tua disciplina. ; -)

risposta data 21.08.2011 - 15:49
fonte
8

Se stai provando prima con Test Driven Development, allora la tua copertura aumenterà nell'intervallo del 90% o superiore, perché non aggiungerai funzionalità senza prima aver scritto un test dell'unità guasto per questo.

Se aggiungi dei test dopo il fatto, allora non posso raccomandare abbastanza che tu riceva una copia di Lavorare efficacemente con il codice legacy di Michael Feathers e dai un'occhiata ad alcune delle tecniche per aggiungere test al tuo codice e metodi di refactoring del tuo codice per renderlo più testabile.

    
risposta data 03.09.2010 - 20:00
fonte
5

Se inizi a seguire le pratiche Test Driven Development , ordineranno guide a te attraverso il processo e sapendo cosa testare verrà naturale. Alcuni posti da cui iniziare:

I test vengono prima

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

Scrivi test di regressione

Ogni volta che incontri un bug, scrivi una testcase e assicurati che fallisca . A meno che tu non riesca a riprodurre un bug attraverso un testcase in errore, non l'hai trovato veramente.

Rosso-Verde-Refactor-Repeat

Rosso : inizia scrivendo un test di base per il comportamento che stai tentando di implementare. Pensa a questo passaggio quando scrivi del codice di esempio che utilizza la classe o la funzione su cui stai lavorando. Assicurati che compaia / 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 apprendere qui è che a meno che 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 qualche motivo falso.

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

Refactor : ripulisci il tuo codice, ottieni i nomi dei metodi corretti. Vedi se il test sta ancora passando. Ottimizzare. Esegui nuovamente il test.

Ripeti : ti ricordi il caso limite che il test non copriva, giusto? Quindi, ora è il suo grande momento. Scrivi una prova che copra quella situazione, guardala fallire, scrivi del codice, guardalo passare, refactoring.

Prova il tuo codice

Stai lavorando su un pezzo di codice specifico e questo è esattamente ciò che vuoi testare. Ciò 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 le API web esterne, alcune risorse intensive del database, ecc. Ogni volta che puoi provare a prenderlo in giro (crea un oggetto che segue la stessa interfaccia, ma restituisce dati statici e predefiniti).

    
risposta data 13.10.2010 - 02:08
fonte
3

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

Inizia con quello. Man mano che acquisisci esperienza nel fare questo test di base, inizierai a capire se è sufficiente o meno e inizi a vedere altri aspetti del tuo codice che devono essere testati.

    
risposta data 22.08.2011 - 00:25
fonte
0

La risposta stock è "prova tutto ciò che potrebbe interrompersi" .

Che cosa è troppo semplice da rompere? Campi dati, accessorie alle proprietà cerebrali e overhead simile. Qualsiasi altra cosa probabilmente implementa una parte identificabile di un requisito e può trarre vantaggio dall'essere testata.

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

    
risposta data 25.09.2010 - 00:01
fonte

Leggi altre domande sui tag