modulo 13 di 24 / Ore 21-40

Moduli 9-16 / Checkpoint 40 ore

Testare errori e casi limite

Come scrivere test per i casi limite del Registro — ID inesistente, strumento guasto, prestito già chiuso, nome vuoto — con esempi concreti per ognuno e spiegazione del perché i casi limite sono più importanti del caso felice.

25 minArchitettura del software

Testare errori e casi limite

Questa lezione appartiene al Modulo 13 — Testare l’architettura.

Il caso felice — lo strumento esiste, è disponibile, il nome studente è valido — è quasi sempre già coperto dal test che hai scritto prima. Ma il sistema del Registro deve comportarsi correttamente anche quando qualcosa va storto. I casi limite sono proprio quelli che rompono il codice scritto frettolosamente.

Obiettivo della lezione

In questa lezione scrivi test per i quattro casi limite principali del Registro: ID inesistente, strumento non disponibile, prestito già chiuso, nome studente vuoto. Impari perché questi casi sono spesso più utili del test del caso felice e come strutturarli in modo leggibile.

Alla fine devi avere un risultato verificabile: quattro test che girano da terminale, ognuno dei quali verifica un caso limite diverso con il messaggio di errore corretto.

Perché i casi limite sono più importanti del caso felice

Il caso felice ti dice che il sistema funziona in condizioni ideali. I casi limite ti dicono che il sistema non si rompe quando le condizioni non sono ideali.

Nel Registro, quasi tutti i bug nascono da casi limite non gestiti:

  • l’utente clicca “Presta” su uno strumento guasto perché la lista non si è aggiornata ancora;
  • qualcuno chiama chiudiPrestito due volte per errore (doppio click);
  • un form viene inviato con il campo nome vuoto perché l’utente ha cancellato il testo;
  • un ID viene passato da una parte del codice all’altra ma lo strumento è stato eliminato nel frattempo.

Nessuno di questi è un errore dell’utente: sono situazioni reali che il sistema deve gestire senza crashare.

I quattro casi limite da testare

CASO 1 — ID inesistente
  Azione: registraPrestito('id-che-non-esiste', 'Mario Rossi')
  Atteso: Error('Strumento non trovato')

CASO 2 — Strumento non disponibile (guasto)
  Setup: strumento con stato 'guasto'
  Azione: registraPrestito('id-guasto', 'Mario Rossi')
  Atteso: Error('Lo strumento non è disponibile per il prestito')

CASO 3 — Prestito già chiuso
  Setup: prestito con stato 'chiuso'
  Azione: chiudiPrestito('prest-chiuso')
  Atteso: Error contenente 'già chiuso'

CASO 4 — Nome studente vuoto
  Azione: registraPrestito('id-disponibile', '')
  Atteso: Error('Il nome dello studente è obbligatorio')

Procedura guidata

  1. Crea il file test/casiLimite.test.js.
  2. Scrivi il test per ID inesistente: archivio vuoto, chiama registraPrestito, verifica l’errore.
  3. Scrivi il test per strumento guasto: archivio con strumento guasto, verifica il messaggio di errore.
  4. Scrivi il test per prestito già chiuso: archivio con prestito chiuso, chiama chiudiPrestito, verifica.
  5. Scrivi il test per nome vuoto: archivio con strumento disponibile, chiama con nome '', verifica.
  6. Esegui tutti e quattro e leggi i messaggi di errore restituiti.
  7. Se un messaggio di errore è diverso da quello atteso, aggiusta il caso d’uso — non il test.

Attività in classe

Compila questa scheda prima di scrivere i test:

## Casi limite nel Registro

Caso 1 — ID inesistente
  Messaggio di errore atteso nel codice: _______________
  File dove viene lanciato: _______________

Caso 2 — Strumento guasto
  Messaggio di errore atteso nel codice: _______________
  File dove viene lanciato: _______________

Caso 3 — Prestito già chiuso
  Messaggio di errore atteso nel codice: _______________
  File dove viene lanciato: _______________

Caso 4 — Nome vuoto
  Messaggio di errore atteso nel codice: _______________
  File dove viene lanciato: _______________

Il caso limite più probabile nella realtà del laboratorio:
  _______________

Se lavori in gruppo, una persona identifica i messaggi di errore nel codice e un’altra scrive i test che li verificano.

Spiegazione guidata per studiare

Un test di caso limite segue sempre la stessa struttura: prepara una situazione anomala, esegui l’azione, verifica che il sistema risponda con il comportamento corretto.

Il “comportamento corretto” non è necessariamente un errore. A volte è un valore vuoto, a volte è una lista vuota, a volte è proprio un’eccezione. L’importante è che sia esplicito e documentato.

Nel Registro, i casi limite più importanti riguardano la coerenza dello stato. Se un test verifica solo che il prestito viene creato, non ti dice cosa succede quando lo stesso strumento viene prestato due volte. Ma quell’errore è possibile in produzione: due utenti che premono “Presta” in rapida successione, o un bug di sincronizzazione tra pagine.

Il pattern per testare gli errori è sempre questo: usa un blocco try/catch, verifica che erroreRicevuto non sia null, verifica che il messaggio contenga la stringa giusta. Non verificare l’intero messaggio: i messaggi cambiano, le parole chiave no.

Quando scrivi i test, inizia sempre dal messaggio di errore. Apri il caso d’uso, trova il throw new Error(...) corrispondente, copia le parole chiave nel test. Poi esegui il test prima di implementare la correzione: deve fallire. Poi correggi il codice e il test deve passare. Questo è il ciclo corretto.

Passo per passo nel progetto

  1. Apri application/registraPrestito.js e trova tutti i throw new Error(...). Annota i messaggi.
  2. Apri application/chiudiPrestito.js (o restituisciPrestito.js) e fai lo stesso.
  3. Per ogni errore trovato, scrivi un test che verifica quel preciso messaggio.
  4. Verifica che il caso “nome vuoto” sia gestito: se non c’è il controllo, aggiungilo prima di scrivere il test.
  5. Esegui i test uno alla volta e osserva il messaggio di errore quando falliscono.
  6. Correggi i messaggi nel codice se non corrispondono a quanto hai scritto nel test.

Esempio da leggere lentamente

Questi quattro test coprono i quattro casi limite del Registro:

// test/casiLimite.test.js
import { registraPrestito } from '../application/registraPrestito.js';
import { chiudiPrestito } from '../application/chiudiPrestito.js';
import {
  creaArchivioStrumentiInMemoria,
  creaArchivioPrestitiInMemoria
} from './supporto/archivioInMemoria.js';

function verificaErrore(fn, parolaChiave) {
  let erroreRicevuto = null;
  try { fn(); } catch (e) { erroreRicevuto = e; }
  if (!erroreRicevuto) throw new Error('Il test doveva lanciare un errore');
  if (!erroreRicevuto.message.includes(parolaChiave)) {
    throw new Error(
      `Messaggio atteso: "${parolaChiave}", ricevuto: "${erroreRicevuto.message}"`
    );
  }
}

function testIdInesistente() {
  const archivioStr = creaArchivioStrumentiInMemoria([]); // nessuno strumento
  const archivioPrest = creaArchivioPrestitiInMemoria([]);
  verificaErrore(
    () => registraPrestito('id-inesistente', 'Mario Rossi', archivioStr, archivioPrest),
    'non trovato'
  );
  console.log('OK — ID inesistente rifiutato');
}

function testStrumentoGuasto() {
  const archivioStr = creaArchivioStrumentiInMemoria([
    { id: 'id-1', nome: 'Trapano', stato: 'guasto' }
  ]);
  const archivioPrest = creaArchivioPrestitiInMemoria([]);
  verificaErrore(
    () => registraPrestito('id-1', 'Mario Rossi', archivioStr, archivioPrest),
    'non è disponibile'
  );
  console.log('OK — strumento guasto rifiutato');
}

function testPrestitoGiaChiuso() {
  const archivioStr = creaArchivioStrumentiInMemoria([
    { id: 'id-1', nome: 'Trapano', stato: 'disponibile' }
  ]);
  const archivioPrest = creaArchivioPrestitiInMemoria([
    { id: 'prest-1', idStrumento: 'id-1', nomeStudente: 'Mario Rossi',
      dataApertura: '2026-01-01T10:00:00.000Z',
      dataChiusura: '2026-01-02T10:00:00.000Z',
      stato: 'chiuso' }
  ]);
  verificaErrore(
    () => chiudiPrestito('prest-1', archivioStr, archivioPrest),
    'già chiuso'
  );
  console.log('OK — chiusura di prestito già chiuso rifiutata');
}

function testNomeVuoto() {
  const archivioStr = creaArchivioStrumentiInMemoria([
    { id: 'id-1', nome: 'Trapano', stato: 'disponibile' }
  ]);
  const archivioPrest = creaArchivioPrestitiInMemoria([]);
  verificaErrore(
    () => registraPrestito('id-1', '', archivioStr, archivioPrest),
    'obbligatorio'
  );
  console.log('OK — nome vuoto rifiutato');
}

testIdInesistente();
testStrumentoGuasto();
testPrestitoGiaChiuso();
testNomeVuoto();
console.log('\nTutti i test di caso limite passati.');

Nota la funzione helper verificaErrore: evita la ripetizione del pattern try/catch in ogni test. Riceve la funzione da eseguire e la parola chiave da cercare nel messaggio di errore.

Prova di comprensione

Prima di chiudere la lezione, rispondi per iscritto a queste domande:

  • Perché verifichiamo una parola chiave nel messaggio di errore invece del messaggio esatto?
  • Se un test di caso limite passa senza che il caso d’uso lanci nessun errore, cosa significa?
  • Quale dei quattro casi limite è il più probabile nell’uso reale del Registro scolastico?
  • Se registraPrestito non gestisce il nome vuoto, dove aggiungi il controllo?

Una risposta accettabile usa i nomi reali dei file e delle funzioni del progetto.

Errori da evitare

  • scrivere il test dopo aver già implementato la gestione dell’errore — rischi di scrivere un test che passa sempre perché è adattato al codice e non al comportamento atteso;
  • verificare il messaggio di errore esatto invece di una parola chiave — i messaggi cambiano, le parole chiave no;
  • non testare il caso “nome vuoto” — è il caso limite più frequente con i form HTML;
  • usare un archivio condiviso tra test — un test che fallisce potrebbe lasciare dati che alterano il test successivo;
  • confondere “il test non lancia errori” con “il codice funziona” — se non c’è un’asserzione, il test non verifica nulla.

Prodotto da consegnare

Crea il file test/casiLimite.test.js con i quattro test descritti in questa lezione. Il file deve girare con node test/casiLimite.test.js senza errori e stampare quattro righe “OK”.

Checklist di chiusura

Prima di chiudere la lezione, controlla:

  • il test per ID inesistente verifica il messaggio “non trovato”;
  • il test per strumento guasto verifica il messaggio “non è disponibile”;
  • il test per prestito già chiuso verifica la parola “già chiuso”;
  • il test per nome vuoto verifica la parola “obbligatorio”;
  • ogni test usa un archivio fresco, non condiviso.

Risultato atteso

Alla fine lo studente deve saper dire:

Ho scritto quattro test per i casi limite del Registro.
ID inesistente: archivio vuoto, registro un prestito, verifico l'errore "non trovato".
Strumento guasto: archivio con stato 'guasto', verifico "non è disponibile".
Prestito già chiuso: prestito con stato 'chiuso', chiudo di nuovo, verifico "già chiuso".
Nome vuoto: strumento disponibile, nome '', verifico "obbligatorio".
I casi limite sono più utili del caso felice perché coprono le situazioni reali di errore.

Se questa spiegazione non è possibile, la lezione non è ancora davvero conclusa.