modulo 13 di 24 / Ore 21-40

Moduli 9-16 / Checkpoint 40 ore

Testare apertura e chiusura di un prestito

Come scrivere test concreti per aprire e chiudere un prestito nel Registro — con archivio in memoria, verifica dello stato dello strumento dopo il prestito, e verifica che chiudere un prestito già chiuso lanci l'errore corretto.

25 min Architettura del software

Testare apertura e chiusura di un prestito

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

Un prestito ha due operazioni principali: l’apertura e la chiusura. Per ognuna devi avere un test che verifica il comportamento atteso. Non basta che “funzioni nel browser”: devi poter eseguire il test da terminale, senza UI, senza localStorage, e ottenere una risposta precisa.

Obiettivo della lezione

In questa lezione scrivi test concreti per registraPrestito e chiudiPrestito usando un archivio in memoria. Verifichi che lo stato dello strumento cambi correttamente, che il prestito venga salvato con i campi giusti, e che tentare di chiudere un prestito già chiuso lanci l’errore corretto.

Alla fine devi avere un risultato verificabile: almeno quattro test che girano con node da terminale.

Le quattro verifiche da fare

Per ogni test devi sapere in anticipo cosa stai verificando. Ecco le quattro verifiche principali:

TEST 1 — Apertura prestito
  Input: strumento disponibile, nome studente valido
  Verifica 1: il prestito restituito ha stato 'aperto'
  Verifica 2: lo strumento nell'archivio ha stato 'in_prestito'
  Verifica 3: il prestito ha nomeStudente, dataApertura, idStrumento

TEST 2 — Chiusura prestito
  Input: prestito aperto esistente
  Verifica 1: il prestito nell'archivio ha stato 'chiuso'
  Verifica 2: lo strumento ha stato 'disponibile'
  Verifica 3: il prestito ha una dataChiusura

TEST 3 — Chiusura di un prestito già chiuso
  Input: prestito con stato 'chiuso'
  Verifica: viene lanciato un errore con messaggio appropriato

TEST 4 — Apertura su strumento non disponibile
  Input: strumento con stato 'in_prestito'
  Verifica: viene lanciato un errore "Lo strumento non è disponibile"

L’archivio in memoria per i test

Per testare senza localStorage, costruisci un archivio in memoria che implementa la stessa interfaccia di quello reale:

// test/supporto/archivioInMemoria.js

function creaArchivioStrumentiInMemoria(strumentiIniziali = []) {
  const strumenti = [...strumentiIniziali];
  return {
    trova(id) {
      return strumenti.find(s => s.id === id) || null;
    },
    salva(strumento) {
      strumenti.push(strumento);
    },
    aggiornaStato(id, nuovoStato) {
      const s = strumenti.find(s => s.id === id);
      if (s) s.stato = nuovoStato;
    },
    tutti() {
      return [...strumenti];
    }
  };
}

function creaArchivioPrestitiInMemoria(prestitiIniziali = []) {
  const prestiti = [...prestitiIniziali];
  return {
    trova(id) {
      return prestiti.find(p => p.id === id) || null;
    },
    salva(prestito) {
      prestiti.push(prestito);
    },
    aggiorna(id, modifiche) {
      const p = prestiti.find(p => p.id === id);
      if (p) Object.assign(p, modifiche);
    },
    tutti() {
      return [...prestiti];
    }
  };
}

Questo archivio non tocca localStorage. Funziona con un array in memoria. È testabile, prevedibile, resettabile a ogni test.

Procedura guidata

  1. Crea il file test/supporto/archivioInMemoria.js con le due funzioni di factory.
  2. Crea il file test/prestito.test.js con i quattro test.
  3. Per ogni test, crea un archivio fresco (non condividere lo stesso archivio tra test).
  4. Esegui il primo test: apertura del prestito. Verifica lo stato dello strumento.
  5. Esegui il secondo test: chiusura del prestito. Verifica che lo strumento torni disponibile.
  6. Esegui il terzo test: chiusura di un prestito già chiuso. Verifica il lancio dell’errore.
  7. Esegui node test/prestito.test.js da terminale e verifica che tutti e quattro i test passino.

Attività in classe

Scrivi il comportamento atteso prima del codice:

## Test per apertura e chiusura prestito

Test 1 — Apertura
  Setup: strumento { id: 'id-1', nome: 'Trapano', stato: 'disponibile' }
  Azione: registraPrestito('id-1', 'Mario Rossi')
  Verifica A: prestito.stato === _______________
  Verifica B: strumento.stato === _______________
  Verifica C: prestito.nomeStudente === _______________

Test 2 — Chiusura
  Setup: prestito aperto con id 'prest-1', strumento 'id-1' in_prestito
  Azione: chiudiPrestito('prest-1')
  Verifica A: prestito.stato === _______________
  Verifica B: strumento.stato === _______________
  Verifica C: prestito.dataChiusura !== _______________

Test 3 — Chiusura già chiuso
  Setup: prestito con stato 'chiuso'
  Azione: chiudiPrestito('prest-1')
  Verifica: errore.message contiene _______________

Spiegazione guidata per studiare

Un test di comportamento verifica tre cose: che l’azione sia possibile, che l’azione produca l’effetto atteso, e che le azioni non permesse vengano rifiutate.

Per l’apertura di un prestito, l’effetto atteso è doppio: il prestito viene creato e lo strumento cambia stato. Non basta verificare che il prestito esista — devi anche verificare che lo strumento sia passato da “disponibile” a “in_prestito”. Se uno dei due non avviene, c’è un bug.

Per la chiusura, il processo è simmetrico: il prestito passa da “aperto” a “chiuso” e lo strumento torna “disponibile”. In più, il prestito deve avere una dataChiusura.

Il test più importante è spesso quello dell’errore: cosa succede quando cerchi di chiudere un prestito già chiuso? Il sistema deve rifiutare l’operazione con un messaggio chiaro. Se non lo rifiuta, due chiamate consecutive a chiudiPrestito producono uno stato inconsistente.

Il pattern con archivio in memoria è il modo più pulito per scrivere questi test. Ogni test parte con un archivio fresco, nessun dato residuo da esecuzioni precedenti, nessuna dipendenza da localStorage. Il test è deterministico: produce sempre lo stesso risultato.

Passo per passo nel progetto

  1. Apri application/registraPrestito.js e identifica cosa viene salvato negli archivi.
  2. Apri (o crea) application/chiudiPrestito.js e identifica cosa modifica.
  3. Crea test/supporto/archivioInMemoria.js con le funzioni di factory.
  4. Scrivi il test 1: crea un archivio con uno strumento disponibile, chiama registraPrestito, verifica.
  5. Scrivi il test 2: crea un archivio con un prestito aperto e lo strumento in_prestito, chiama chiudiPrestito, verifica.
  6. Scrivi il test 3: crea un archivio con un prestito già chiuso, chiama chiudiPrestito, verifica l’errore.
  7. Esegui node test/prestito.test.js e leggi l’output.

Esempio da leggere lentamente

Questi test usano l’archivio in memoria per verificare apertura e chiusura:

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

function testAperturaPrestito() {
  const archivioStr = creaArchivioStrumentiInMemoria([
    { id: 'id-1', nome: 'Trapano Bosch', stato: 'disponibile' }
  ]);
  const archivioPrest = creaArchivioPrestitiInMemoria([]);

  const prestito = registraPrestito('id-1', 'Mario Rossi', archivioStr, archivioPrest);

  // verifica il prestito
  if (prestito.stato !== 'aperto') throw new Error('Il prestito deve essere aperto');
  if (prestito.nomeStudente !== 'Mario Rossi') throw new Error('Nome studente errato');
  if (!prestito.dataApertura) throw new Error('Manca la dataApertura');

  // verifica lo stato dello strumento
  const strumento = archivioStr.trova('id-1');
  if (strumento.stato !== 'in_prestito') {
    throw new Error('Lo strumento deve essere in_prestito dopo il prestito');
  }

  console.log('OK — apertura prestito');
}

function testChiusuraPrestito() {
  const archivioStr = creaArchivioStrumentiInMemoria([
    { id: 'id-1', nome: 'Trapano Bosch', stato: 'in_prestito' }
  ]);
  const archivioPrest = creaArchivioPrestitiInMemoria([
    { id: 'prest-1', idStrumento: 'id-1', nomeStudente: 'Mario Rossi',
      dataApertura: '2026-01-01T10:00:00.000Z', stato: 'aperto' }
  ]);

  chiudiPrestito('prest-1', archivioStr, archivioPrest);

  const prestito = archivioPrest.trova('prest-1');
  if (prestito.stato !== 'chiuso') throw new Error('Il prestito deve essere chiuso');
  if (!prestito.dataChiusura) throw new Error('Manca la dataChiusura');

  const strumento = archivioStr.trova('id-1');
  if (strumento.stato !== 'disponibile') {
    throw new Error('Lo strumento deve tornare disponibile');
  }

  console.log('OK — chiusura prestito');
}

function testChiusuraGiaChiuso() {
  const archivioStr = creaArchivioStrumentiInMemoria([
    { id: 'id-1', nome: 'Trapano Bosch', 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' }
  ]);

  let erroreRicevuto = null;
  try {
    chiudiPrestito('prest-1', archivioStr, archivioPrest);
  } catch (e) {
    erroreRicevuto = e;
  }

  if (!erroreRicevuto) throw new Error('Il test doveva lanciare un errore');
  if (!erroreRicevuto.message.includes('già chiuso')) {
    throw new Error('Messaggio di errore non corretto: ' + erroreRicevuto.message);
  }

  console.log('OK — chiusura di prestito già chiuso rifiutata');
}

testAperturaPrestito();
testChiusuraPrestito();
testChiusuraGiaChiuso();
console.log('\nTutti i test passati.');

Prova di comprensione

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

  • Perché creare un archivio fresco per ogni test invece di condividerne uno?
  • Quali due archivi devono essere aggiornati quando si apre un prestito?
  • Se chiudiPrestito non aggiorna dataChiusura, quale test lo scopre?
  • Cosa succede se esegui testChiusuraGiaChiuso due volte con lo stesso archivio condiviso?

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

Errori da evitare

  • condividere lo stesso archivio tra test diversi — il test 2 troverebbe dati lasciati dal test 1;
  • verificare solo il prestito senza verificare lo stato dello strumento;
  • non testare il caso di errore — chiudere un prestito già chiuso è il caso limite più comune;
  • passare null come archivio senza gestirlo nel caso d’uso;
  • non avere dataChiusura nel test di chiusura — è il campo che distingue aperto da chiuso.

Prodotto da consegnare

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

Checklist di chiusura

Prima di chiudere la lezione, controlla:

  • il test di apertura verifica sia il prestito sia lo stato dello strumento;
  • il test di chiusura verifica che lo strumento torni disponibile;
  • il test di chiusura verifica la presenza di dataChiusura;
  • il test di chiusura già chiusa verifica il messaggio di errore;
  • ogni test usa un archivio fresco, non condiviso.

Risultato atteso

Alla fine lo studente deve saper dire:

Ho scritto tre test per apertura e chiusura di un prestito.
Ogni test crea un archivio in memoria fresco con i dati di partenza.
Il test di apertura verifica il prestito creato e il cambio di stato dello strumento.
Il test di chiusura verifica stato 'chiuso', dataChiusura e che lo strumento torni disponibile.
Il test di errore verifica che chiudere un prestito già chiuso lanci l'eccezione corretta.

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