modulo 23 di 24 / Ore 41-60

Moduli 17-24 / Checkpoint 60 ore

Controllare test, errori e dati di esempio

Come preparare dati di esempio realistici per il testing del Registro e verificare che gli errori vengano gestiti correttamente in ogni scenario.

30 min Architettura del software

Controllare test, errori e dati di esempio

Questa lezione appartiene al Modulo 23 — Revisione finale.

Il lavoro continua il progetto guida, il Registro del laboratorio scolastico. In questa lezione prepari un set di dati di esempio realistici e verifichi che ogni tipo di errore venga gestito correttamente dal sistema.

Obiettivo della lezione

Costruire un set di dati di esempio che copre tutti gli stati del Registro (strumenti disponibili, in prestito, guasti; prestiti aperti e chiusi) e usarlo per verificare che i test e la gestione degli errori siano completi.

Alla fine devi avere: un file dati-esempio.js con dati realistici e una verifica documentata che ogni errore viene gestito nel punto corretto.

Perché i dati di esempio sono importanti

Un test che usa dati inventati al momento (es. id: "x") è difficile da leggere e da mantenere. Dati di esempio ben costruiti permettono di:

  • testare scenari complessi con pochi righe di setup
  • riutilizzare gli stessi dati in test diversi
  • capire subito cosa rappresenta ogni record
  • caricare rapidamente la demo con uno scenario realistico

Cosa deve coprire il set di dati

Per testare il Registro in modo completo, il set di dati deve avere almeno:

  • 2-3 strumenti in stato "disponibile"
  • 1-2 strumenti in stato "in-prestito" con prestiti aperti corrispondenti
  • 1 strumento in stato "guasto"
  • 1 prestito chiuso (con dataChiusura non null)
  • 2-3 categorie diverse (misura, saldatura, perforazione, ecc.)

Procedura guidata

  1. Crea src/test/dati-esempio.js con il set di dati completo.
  2. Crea src/test/test-con-dati-esempio.js che importa i dati e li usa nei test.
  3. Verifica che ogni tipo di errore del Registro abbia un test con dati realistici.
  4. Verifica che gli errori vengano gestiti nel livello corretto (Application, non Presentation).
  5. Esegui i test con Node.js e documenta i risultati.

Attività in classe

Compila questa scheda prima di scrivere i dati di esempio:

## Dati di esempio — pianificazione

Strumenti da includere:
1. (nome, categoria, stato)
2.
3.
4.
5.

Prestiti da includere:
1. (strumentoId, stato, aperto/chiuso)
2.

Errori da testare:
1. (scenario, errore atteso)
2.
3.

Spiegazione guidata per studiare

Dati di esempio realistici riducono la distanza tra test e produzione. Se il tuo test usa { id: "s1", nome: "Strumento test" } stai testando un caso troppo semplice. Se usa { id: "multimetro-fluke-87v", nome: "Multimetro Fluke 87V", categoria: "misura", stato: "in-prestito" } stai testando qualcosa che assomiglia al mondo reale.

La gestione degli errori si verifica in due posti distinti: il caso d’uso lancia l’errore con un codice specifico, la Presentation lo intercetta e lo converte in messaggio leggibile. Devi verificare entrambi i livelli — non solo che l’errore viene lanciato, ma anche che viene mostrato correttamente all’utente.

Passo per passo nel progetto

  1. Crea src/test/dati-esempio.js:
// src/test/dati-esempio.js

export const strumentiEsempio = [
  {
    id: "str-001",
    nome: "Multimetro digitale",
    categoria: "misura",
    stato: "disponibile"
  },
  {
    id: "str-002",
    nome: "Oscilloscopio 2 canali",
    categoria: "misura",
    stato: "in-prestito"
  },
  {
    id: "str-003",
    nome: "Saldatore a stazione",
    categoria: "saldatura",
    stato: "disponibile"
  },
  {
    id: "str-004",
    nome: "Trapano a colonna",
    categoria: "perforazione",
    stato: "guasto"
  },
  {
    id: "str-005",
    nome: "Alimentatore da banco",
    categoria: "alimentazione",
    stato: "disponibile"
  }
];

export const prestitiEsempio = [
  {
    id: "pre-001",
    strumentoId: "str-002",
    dataApertura: "2025-10-01",
    dataChiusura: null // prestito ancora aperto
  },
  {
    id: "pre-002",
    strumentoId: "str-001",
    dataApertura: "2025-09-15",
    dataChiusura: "2025-09-20" // prestito chiuso
  }
];
  1. Crea src/test/test-con-dati-esempio.js:
// src/test/test-con-dati-esempio.js
import { strumentiEsempio, prestitiEsempio } from "./dati-esempio.js";
import { apriPrestito } from "../application/apriPrestito.js";
import { chiudiPrestito } from "../application/chiudiPrestito.js";
import { segnalaGuasto } from "../application/segnalaGuasto.js";
import { cercaStrumenti } from "../application/cercaStrumenti.js";

function creaArchivio(datiIniziali) {
  let dati = datiIniziali.map(d => ({ ...d })); // copia profonda
  return {
    caricaTutti: () => dati.map(d => ({ ...d })),
    salva: (nuovi) => { dati = nuovi.map(d => ({ ...d })); },
    trovaPerId: (id) => {
      const trovato = dati.find(d => d.id === id);
      return trovato ? { ...trovato } : null;
    }
  };
}

function ok(desc) { console.log("OK:", desc); }
function ko(desc, e) { console.error("FALLITO:", desc, e?.message ?? e); }

// test 1: cerca strumenti disponibili
try {
  const archivio = creaArchivio(strumentiEsempio);
  const disponibili = cercaStrumenti({ filtroStato: "disponibile" }, archivio);
  if (disponibili.length !== 3) throw new Error(`attesi 3, trovati ${disponibili.length}`);
  ok("cerca disponibili → 3 strumenti");
} catch (e) { ko("cerca disponibili", e); }

// test 2: apri prestito su strumento disponibile
try {
  const archivio = creaArchivio(strumentiEsempio);
  const archivioP = creaArchivio([]);
  const prestito = apriPrestito({ strumentoId: "str-001" }, archivio, archivioP);
  if (!prestito.id) throw new Error("prestito senza id");
  ok("apri prestito su str-001 → OK");
} catch (e) { ko("apri prestito disponibile", e); }

// test 3: apri prestito su strumento già in prestito
try {
  const archivio = creaArchivio(strumentiEsempio);
  const archivioP = creaArchivio(prestitiEsempio);
  apriPrestito({ strumentoId: "str-002" }, archivio, archivioP);
  ko("prestito su str-002 in prestito", new Error("avrebbe dovuto fallire"));
} catch (e) {
  if (e.message === "STRUMENTO_NON_DISPONIBILE") ok("prestito su str-002 → STRUMENTO_NON_DISPONIBILE");
  else ko("prestito su str-002 — errore sbagliato", e);
}

// test 4: segnala guasto su strumento già guasto
try {
  const archivio = creaArchivio(strumentiEsempio);
  segnalaGuasto({ strumentoId: "str-004" }, archivio);
  ko("guasto su str-004 già guasto", new Error("avrebbe dovuto fallire"));
} catch (e) {
  if (e.message === "STRUMENTO_GIA_GUASTO") ok("guasto su str-004 → STRUMENTO_GIA_GUASTO");
  else ko("guasto su str-004 — errore sbagliato", e);
}

// test 5: chiudi prestito aperto
try {
  const archivio = creaArchivio(strumentiEsempio);
  const archivioP = creaArchivio(prestitiEsempio);
  chiudiPrestito({ strumentoId: "str-002" }, archivio, archivioP);
  const strumentoAggiornato = archivio.trovaPerId("str-002");
  if (strumentoAggiornato.stato !== "disponibile") throw new Error("stato non aggiornato");
  ok("chiudi prestito str-002 → torna disponibile");
} catch (e) { ko("chiudi prestito str-002", e); }
  1. Esegui con node src/test/test-con-dati-esempio.js e leggi l’output.

Esempio JS — verifica gestione errori nella Presentation

Oltre ai test dei casi d’uso, verifica che i messaggi di errore nella UI siano corretti:

// verifica manuale nella UI — lista degli errori da testare

const scenariErrore = [
  {
    scenario: "Presta uno strumento con ID vuoto",
    passi: ["lascia il campo ID vuoto", "clicca Presta"],
    messaggioAtteso: "Inserisci un ID strumento valido."
  },
  {
    scenario: "Presta uno strumento con ID inesistente",
    passi: ["inserisci ID 'xyz'", "clicca Presta"],
    messaggioAtteso: "Strumento non trovato."
  },
  {
    scenario: "Presta uno strumento già in prestito",
    passi: ["inserisci ID 'str-002'", "clicca Presta"],
    messaggioAtteso: "Lo strumento non è disponibile."
  },
  {
    scenario: "Restituisci uno strumento senza prestiti aperti",
    passi: ["inserisci ID 'str-001' (disponibile)", "clicca Restituisci"],
    messaggioAtteso: "Nessun prestito aperto per questo strumento."
  }
];

// per ogni scenario: eseguilo manualmente nel browser e verifica il messaggio

Prova di comprensione

Rispondi per iscritto:

  • Perché creaArchivio fa una copia con map(d => ({ ...d })) invece di passare il riferimento?
  • Qual è la differenza tra testare l’errore nel caso d’uso e verificare il messaggio nella UI?
  • Se aggiungi un nuovo tipo di strumento con stato "in-manutenzione", quali test esistenti potrebbero cambiare?
  • Come useresti dati-esempio.js anche per caricare i dati nella demo, non solo nei test?

Errori da evitare

  • usare dati troppo semplici (id: “a”, nome: “test”) che non scoprono bug reali;
  • non verificare che l’errore specifico sia quello atteso (solo “ha lanciato un errore qualunque”);
  • dimenticare di copiare i dati di esempio — la modifica di un test non deve influire sugli altri;
  • verificare solo il percorso felice e ignorare gli scenari di errore.

Prodotto da consegnare

I file src/test/dati-esempio.js e src/test/test-con-dati-esempio.js funzionanti, con almeno cinque test che usano i dati realistici. Come verifica: esegui node src/test/test-con-dati-esempio.js e mostra tutti i “OK” al docente.

Checklist di chiusura

  • dati-esempio.js copre tutti gli stati: disponibile, in-prestito, guasto, prestito aperto, prestito chiuso;
  • ogni test usa i dati di esempio e non crea dati al volo;
  • ogni test verifica il tipo specifico di errore, non solo che sia stato lanciato;
  • la funzione creaArchivio copia i dati per isolare i test;
  • tutti i test passano con node.

Risultato atteso

Ho creato dati-esempio.js con 5 strumenti e 2 prestiti che coprono tutti gli stati.
Ho scritto 5 test con dati realistici — tutti passano.
Ho verificato manualmente i messaggi di errore nella UI per 4 scenari.
I dati di esempio verranno usati anche per la demo finale.