Dai casi di test alla diagnosi di un errore

Casi attesi, test mirati e ipotesi verificabili rendono più ordinata la correzione dei difetti.

Un test mette a confronto il comportamento osservato di un componente con quello atteso per un input dichiarato. Prima di eseguire il codice, si stabilisce il risultato corretto; altrimenti il test rischia di limitarsi a confermare ciò che il programma già fa. Ad esempio, una funzione che conta elementi positivi deve dare 2 per [-1, 4, 0, 7]. Il test dovrebbe verificare precisamente il valore restituito e, se utile, anche il trattamento degli input vuoti o non validi.

Una buona selezione include casi ordinari, casi limite e situazioni che potrebbero smentire le assunzioni. Se si testa una ricerca in una sequenza, sono diversi i casi “elemento presente”, “assente”, “primo elemento”, “ultimo elemento” e “sequenza vuota”. Non occorre provare ogni input possibile, spesso impraticabile: occorre scegliere casi informativi. Ogni test documenta una proprietà concreta, mentre il successo dei test non garantisce da solo l’assenza di difetti in scenari non provati.

Il debugging è un’indagine. Si riproduce il problema con un input minimo, si annota il risultato atteso e quello ottenuto, e si formula un’ipotesi sulle cause. Poi si cerca una prova che distingua quell’ipotesi dalle alternative: un valore intermedio osservato o un test aggiuntivo. Se la previsione non si verifica, si rivede l’ipotesi invece di cambiare codice a caso. Asserzioni e registrazioni possono restringere il tratto di esecuzione in cui compare la divergenza.

Per esempio, se il conteggio restituisce 1 invece di 2, si può sospettare che il ciclo termini troppo presto. Un test con due valori positivi consecutivi e uno con i positivi separati verifica la previsione. Una correzione va accompagnata dal test che riproduceva il difetto, così un cambiamento futuro non reintroduce facilmente lo stesso problema. La documentazione utile registra scopo, input, aspettativa e limiti del test, non proclama che il programma sia infallibile.

  • Un test confronta un risultato osservato con un’aspettativa esplicita.
  • Casi ordinari, limite e contrari coprono comportamenti differenti.
  • Il debugging procede per riproduzione, ipotesi e verifiche, non per tentativi casuali.

Metti alla prova quello che hai letto

In un test unitario, che cosa rende significativo il confronto?

  1. L’aspettativa è definita prima del confronto
  2. Il programma viene eseguito molte volte senza input
  3. Si cambia il codice fino a ottenere un risultato qualsiasi
  4. Si prova soltanto il caso più comune
Soluzione e spiegazione

L’aspettativa è definita prima del confronto. Un test controlla se il valore osservato coincide con quello atteso, definito in base al comportamento richiesto.

Fonti

  1. unittest — Unit testing framework — Python Software Foundation
  2. Introduction to Debugging — The Debugging Book, Saarland University

Nota editoriale

Confronto assistito documentato; revisione docente necessaria