modulo 13 di 24 / Ore 21-40

Moduli 9-16 / Checkpoint 40 ore

Testare StatoStrumento e regole di prestito

Come scrivere test per le funzioni di dominio isDisponibile, puoEsserePrestato e le transizioni di stato — con test concreti per ogni stato possibile e per le regole di transizione.

25 min Architettura del software

Testare StatoStrumento e regole di prestito

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

Le funzioni di dominio sono le più facili da testare perché non hanno dipendenze esterne: ricevono un oggetto, applicano una regola, restituiscono un risultato. Non serve un archivio, non serve un browser. Bastano valori di input e un’asserzione.

Obiettivo della lezione

In questa lezione scrivi test concreti per isDisponibile, puoEsserePrestato e verificaTransizione del Registro. Copri tutti e quattro gli stati possibili di uno strumento e tutte le transizioni valide e non valide.

Alla fine devi avere un risultato verificabile: un file di test che gira con node da terminale e copre ogni stato e ogni regola di transizione.

Le funzioni da testare

Prima di scrivere i test, ricorda cosa fa ogni funzione:

isDisponibile(strumento)
  → true se strumento.stato === 'disponibile'
  → false per qualsiasi altro stato

puoEsserePrestato(strumento)
  → true se lo strumento è disponibile
  → false se è in_prestito, guasto o in_manutenzione

verificaTransizione(statoAttuale, statoNuovo)
  → non fa nulla se la transizione è valida
  → lancia un errore se la transizione non è permessa

  Transizioni valide:
  disponibile    → in_prestito     (apertura prestito)
  disponibile    → guasto          (segnalazione guasto)
  in_prestito    → disponibile     (restituzione)
  in_prestito    → guasto          (guasto durante prestito)
  guasto         → in_manutenzione (avvio manutenzione)
  in_manutenzione → disponibile    (manutenzione completata)

  Transizioni non valide:
  guasto         → disponibile     (non si salta la manutenzione)
  in_manutenzione → guasto         (non si regredisce)
  in_prestito    → in_manutenzione (non si manda in manutenzione durante prestito)

I test da scrivere

Ogni funzione richiede un test per ogni percorso possibile:

isDisponibile — 4 test, uno per stato
  disponibile → true
  in_prestito → false
  guasto → false
  in_manutenzione → false

puoEsserePrestato — 4 test, uno per stato
  disponibile → true
  in_prestito → false
  guasto → false
  in_manutenzione → false

verificaTransizione — 6 test per transizioni valide + 3 test per transizioni non valide
  Valide: non lanciano errori
  Non valide: lanciano errori con messaggio leggibile

Procedura guidata

  1. Crea il file test/dominio.test.js.
  2. Importa le funzioni da domain/regolePrestito.js e domain/StatoStrumento.js.
  3. Scrivi i quattro test per isDisponibile, uno per ogni stato.
  4. Scrivi i quattro test per puoEsserePrestato, uno per ogni stato.
  5. Scrivi i sei test per transizioni valide: verificaTransizione non deve lanciare errori.
  6. Scrivi i tre test per transizioni non valide: verificaTransizione deve lanciare errori.
  7. Esegui node test/dominio.test.js e verifica che tutti i test passino.

Attività in classe

Compila questa tabella prima di scrivere il codice:

## Test per le funzioni di dominio

isDisponibile:
  { stato: 'disponibile' }        → _______________
  { stato: 'in_prestito' }        → _______________
  { stato: 'guasto' }             → _______________
  { stato: 'in_manutenzione' }    → _______________

puoEsserePrestato:
  { stato: 'disponibile' }        → _______________
  { stato: 'in_prestito' }        → _______________
  { stato: 'guasto' }             → _______________
  { stato: 'in_manutenzione' }    → _______________

verificaTransizione (valide — nessun errore):
  disponibile    → in_prestito    = _______________
  disponibile    → guasto         = _______________
  in_prestito    → disponibile    = _______________
  in_manutenzione → disponibile   = _______________

verificaTransizione (non valide — errore):
  guasto         → disponibile    = errore: _______________
  in_prestito    → in_manutenzione = errore: _______________

Spiegazione guidata per studiare

Testare le funzioni di dominio è il punto di partenza per qualsiasi suite di test. Sono i test più semplici da scrivere, i più veloci da eseguire e quelli che coprono le regole più importanti del sistema.

isDisponibile e puoEsserePrestato sembrano banali: una riga di codice, una condizione. Ma hanno quattro percorsi possibili (uno per ogni stato) e ognuno deve essere esplicitamente verificato. Se isDisponibile restituisce true per uno strumento guasto a causa di un typo ('guasto' scritto come 'Guasto'), un test ti lo dice subito. Senza test, scopriresti il bug solo quando uno studente tenta di prendere in prestito uno strumento guasto.

verificaTransizione è più interessante. Ha una mappa di transizioni valide e deve lanciare un errore per qualsiasi altra combinazione. Testare le transizioni valide è facile: verifichi che non vengano lanciati errori. Testare le transizioni non valide è più importante: verifichi che il sistema rifiuti esplicitamente le transizioni proibite.

La cosa fondamentale da capire è che i test di dominio non hanno bisogno di archivi né di browser. Ogni test è una funzione che passa un oggetto semplice { stato: 'guasto' } e verifica il valore restituito. Questa semplicità è la prova che il dominio è davvero separato dagli altri livelli.

Passo per passo nel progetto

  1. Apri domain/StatoStrumento.js e copia i quattro valori costanti.
  2. Apri domain/regolePrestito.js e leggi isDisponibile, puoEsserePrestato, verificaTransizione.
  3. Costruisci un oggetto strumento minimale per ogni test: { stato: StatoStrumento.GUASTO }.
  4. Per i test su verificaTransizione, chiama la funzione dentro un try/catch per i casi non validi.
  5. Verifica che i test di transizione valida non lancino errori: se non c’è eccezione, il test passa.
  6. Verifica che i test di transizione non valida lancino errori con il messaggio corretto.
  7. Esegui tutti i test e conta: devono essere almeno tredici (4 + 4 + 6 valide + 3 non valide).

Esempio da leggere lentamente

Questi test coprono tutte le funzioni di dominio del Registro:

// test/dominio.test.js
import { StatoStrumento } from '../domain/StatoStrumento.js';
import {
  isDisponibile,
  puoEsserePrestato,
  verificaTransizione
} from '../domain/regolePrestito.js';

// Helper per asserzioni
function assertEqual(ottenuto, atteso, messaggio) {
  if (ottenuto !== atteso) {
    throw new Error(`${messaggio}: atteso ${atteso}, ottenuto ${ottenuto}`);
  }
}

function assertLanciaErrore(fn, parolaChiave) {
  let errore = null;
  try { fn(); } catch (e) { errore = e; }
  if (!errore) throw new Error('Atteso un errore, ma non è stato lanciato');
  if (!errore.message.includes(parolaChiave)) {
    throw new Error(`Parola chiave "${parolaChiave}" non trovata in: "${errore.message}"`);
  }
}

function assertNonLanciaErrore(fn) {
  try { fn(); } catch (e) {
    throw new Error('Non atteso un errore, ma è stato lanciato: ' + e.message);
  }
}

// Test isDisponibile
assertEqual(isDisponibile({ stato: StatoStrumento.DISPONIBILE }), true,  'disponibile → true');
assertEqual(isDisponibile({ stato: StatoStrumento.IN_PRESTITO }), false, 'in_prestito → false');
assertEqual(isDisponibile({ stato: StatoStrumento.GUASTO }),      false, 'guasto → false');
assertEqual(isDisponibile({ stato: StatoStrumento.IN_MANUTENZIONE }), false, 'in_manutenzione → false');
console.log('OK — isDisponibile (4 test)');

// Test puoEsserePrestato
assertEqual(puoEsserePrestato({ stato: StatoStrumento.DISPONIBILE }),    true,  'disponibile → prestabile');
assertEqual(puoEsserePrestato({ stato: StatoStrumento.IN_PRESTITO }),    false, 'in_prestito → non prestabile');
assertEqual(puoEsserePrestato({ stato: StatoStrumento.GUASTO }),         false, 'guasto → non prestabile');
assertEqual(puoEsserePrestato({ stato: StatoStrumento.IN_MANUTENZIONE }), false, 'in_manutenzione → non prestabile');
console.log('OK — puoEsserePrestato (4 test)');

// Test verificaTransizione — transizioni valide
assertNonLanciaErrore(() => verificaTransizione(StatoStrumento.DISPONIBILE, StatoStrumento.IN_PRESTITO));
assertNonLanciaErrore(() => verificaTransizione(StatoStrumento.DISPONIBILE, StatoStrumento.GUASTO));
assertNonLanciaErrore(() => verificaTransizione(StatoStrumento.IN_PRESTITO, StatoStrumento.DISPONIBILE));
assertNonLanciaErrore(() => verificaTransizione(StatoStrumento.IN_PRESTITO, StatoStrumento.GUASTO));
assertNonLanciaErrore(() => verificaTransizione(StatoStrumento.GUASTO, StatoStrumento.IN_MANUTENZIONE));
assertNonLanciaErrore(() => verificaTransizione(StatoStrumento.IN_MANUTENZIONE, StatoStrumento.DISPONIBILE));
console.log('OK — verificaTransizione transizioni valide (6 test)');

// Test verificaTransizione — transizioni non valide
assertLanciaErrore(
  () => verificaTransizione(StatoStrumento.GUASTO, StatoStrumento.DISPONIBILE),
  'non valida'
);
assertLanciaErrore(
  () => verificaTransizione(StatoStrumento.IN_MANUTENZIONE, StatoStrumento.GUASTO),
  'non valida'
);
assertLanciaErrore(
  () => verificaTransizione(StatoStrumento.IN_PRESTITO, StatoStrumento.IN_MANUTENZIONE),
  'non valida'
);
console.log('OK — verificaTransizione transizioni non valide (3 test)');

console.log('\nTutti i test di dominio passati.');

Nota: nessuna importazione da infrastructure/, nessun archivio, nessun browser. Solo funzioni pure e valori di input.

Prova di comprensione

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

  • Perché i test di dominio non hanno bisogno di un archivio?
  • Cosa significa che verificaTransizione è una funzione “pura”?
  • Se aggiungi un quinto stato 'dismesso' al sistema, quanti test devi aggiungere almeno?
  • Perché testare le transizioni non valide è importante quanto testare quelle valide?

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

Errori da evitare

  • non testare tutti e quattro gli stati per isDisponibile — un typo in una costante sarebbe invisibile;
  • testare solo le transizioni valide e dimenticare quelle non valide;
  • costruire oggetti strumento complessi quando basta { stato: 'guasto' };
  • importare qualcosa da infrastructure/ nel file di test del dominio — non serve mai;
  • non avere la funzione helper assertLanciaErrore e ripetere il pattern try/catch per ogni test.

Prodotto da consegnare

Crea il file test/dominio.test.js con almeno tredici test: quattro per isDisponibile, quattro per puoEsserePrestato, sei per transizioni valide e tre per transizioni non valide. Il file deve girare con node test/dominio.test.js senza errori.

Checklist di chiusura

Prima di chiudere la lezione, controlla:

  • tutti e quattro gli stati sono testati in isDisponibile;
  • tutti e quattro gli stati sono testati in puoEsserePrestato;
  • le sei transizioni valide non lanciano errori;
  • le tre transizioni non valide lanciano errori con il messaggio corretto;
  • il file di test non importa nulla da infrastructure/.

Risultato atteso

Alla fine lo studente deve saper dire:

Ho scritto tredici test per le funzioni di dominio del Registro.
isDisponibile: quattro test, uno per ogni stato — solo 'disponibile' restituisce true.
puoEsserePrestato: quattro test, risultato uguale a isDisponibile per questo progetto.
verificaTransizione: sei test per transizioni valide (nessun errore atteso),
tre test per transizioni non valide (errore atteso con "non valida" nel messaggio).
I test di dominio non usano archivi né browser: sono funzioni pure su oggetti semplici.

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