modulo 13 di 24 / Ore 21-40

Moduli 9-16 / Checkpoint 40 ore

Fake repository in memoria per i test

Come costruire un fake repository in memoria per testare i casi d'uso del Registro: implementazione con array, stesso contratto dell'adapter reale, reset tra test, con codice concreto.

25 minArchitettura del software

Fake repository in memoria per i test

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

Obiettivo della lezione

Costruire un fake repository in memoria che rispetti lo stesso contratto dell’adapter di localStorage, in modo da testare i casi d’uso del Registro senza toccare il browser, localStorage o il DOM.

Alla fine avrai un archivioStrumentiFake e un archivioPrestitiFake usabili nei test, con reset tra un test e l’altro.

Perché serve nel percorso

I casi d’uso (registraStrumento, registraPrestito, chiudiPrestito) ricevono l’archivio come dipendenza. Questo significa che puoi passare un archivio finto nei test — senza bisogno di localStorage, senza browser, senza setup complesso. Il test è veloce, isolato e ripetibile.

Nel Registro del laboratorio

Il contratto dell’archivio strumenti è definito dalle operazioni che i casi d’uso usano:

// Contratto atteso da registraStrumento e cercaStrumenti:
// - salva(strumento): void
// - trovaTutti(): Strumento[]
// - trovaPerId(id): Strumento | undefined
// - aggiorna(strumento): void

Il fake deve implementare esattamente questo contratto usando un array in memoria:

// test/fakes/archivioStrumentiFake.js
export function creaArchivioStrumentiFake(strumentiIniziali = []) {
  let strumenti = [...strumentiIniziali];

  return {
    salva(strumento) {
      strumenti.push({ ...strumento });
    },
    trovaTutti() {
      return [...strumenti];
    },
    trovaPerId(id) {
      return strumenti.find(s => s.id === id);
    },
    aggiorna(strumentoAggiornato) {
      strumenti = strumenti.map(s =>
        s.id === strumentoAggiornato.id ? { ...strumentoAggiornato } : s
      );
    },
    // Helper per i test: resetta lo stato
    _reset(nuoviStrumenti = []) {
      strumenti = [...nuoviStrumenti];
    }
  };
}

Procedura guidata

  1. Crea la cartella test/fakes/ se non esiste.
  2. Crea archivioStrumentiFake.js con la funzione creaArchivioStrumentiFake.
  3. Crea archivioPrestitiFake.js con la funzione creaArchivioPrestitiFake.
  4. Scrivi un test di base che verifica il fake stesso (salva, trova, aggiorna).
  5. Usa i fake in un test di caso d’uso (registraStrumento, registraPrestito).
  6. Verifica che il fake sia resettato tra test diversi.

Attività in classe

Implementa il fake per i prestiti seguendo lo stesso schema:

// test/fakes/archivioPrestitiFake.js
export function creaArchivioPrestitiFake(prestitiIniziali = []) {
  let prestiti = [...prestitiIniziali];

  return {
    salva(prestito) {
      prestiti.push({ ...prestito });
    },
    trovaTutti() {
      return [...prestiti];
    },
    trovaPerId(id) {
      // Scrivi questa riga
    },
    trovaAperti() {
      // Scrivi questa riga: filtra prestiti con chiusura === null
    },
    aggiorna(prestitoAggiornato) {
      // Scrivi questa riga
    }
  };
}

Poi scrivi questo test e verifica che passi:

// test/application/registraPrestito.test.js
import { creaArchivioStrumentiFake } from "../fakes/archivioStrumentiFake.js";
import { creaArchivioPrestitiFake } from "../fakes/archivioPrestitiFake.js";
import { registraPrestito } from "../../src/application/registraPrestito.js";

test("registra un prestito per uno strumento disponibile", () => {
  const strumentiFake = creaArchivioStrumentiFake([
    { id: "str-001", nome: "Oscilloscopio", categoria: "Misura", stato: "disponibile" }
  ]);
  const prestitiFake = creaArchivioPrestitiFake();

  registraPrestito(
    { strumentoId: "str-001", studenteId: "stu-042" },
    strumentiFake,
    prestitiFake
  );

  const prestitiSalvati = prestitiFake.trovaTutti();
  expect(prestitiSalvati).toHaveLength(1);
  expect(prestitiSalvati[0].strumentoId).toBe("str-001");
  expect(prestitiSalvati[0].chiusura).toBeNull();

  // Lo stato dello strumento deve essere aggiornato
  const strumento = strumentiFake.trovaPerId("str-001");
  expect(strumento.stato).toBe("in_prestito");
});

test("lancia errore per uno strumento guasto", () => {
  const strumentiFake = creaArchivioStrumentiFake([
    { id: "str-001", nome: "Trapano", categoria: "Utensile", stato: "guasto" }
  ]);
  const prestitiFake = creaArchivioPrestitiFake();

  expect(() =>
    registraPrestito({ strumentoId: "str-001", studenteId: "stu-042" }, strumentiFake, prestitiFake)
  ).toThrow("STRUMENTO_GUASTO_NON_PRESTABILE");
});

Spiegazione guidata per studiare

Il fake repository è diverso da un mock. Un mock registra le chiamate e le verifica dopo. Un fake è un’implementazione reale che usa la memoria invece del disco — si comporta come l’adapter vero, ma non persiste nulla tra le esecuzioni.

Il vantaggio è che i test parlano in termini di comportamento, non di chiamate:

// Con mock (verifica le chiamate):
expect(archivio.salva).toHaveBeenCalledWith(expect.objectContaining({ stato: "in_prestito" }));

// Con fake (verifica il risultato):
expect(strumentiFake.trovaPerId("str-001").stato).toBe("in_prestito");

Il secondo approccio è più robusto: non fallisce se rinomini il metodo interno o cambi l’implementazione, finché il comportamento finale è lo stesso.

Importante: copia difensiva. Nota che nel fake tutte le operazioni di lettura restituiscono copie ([...strumenti], { ...strumento }). Questo evita che il test possa modificare accidentalmente lo stato interno del fake attraverso l’oggetto restituito.

Reset tra test. Se usi Jest o Vitest, ogni test deve partire da uno stato pulito. Il modo più semplice è creare un nuovo fake in ogni test, invece di condividere un’istanza globale:

// NON fare così — lo stato si accumula tra test
const archivio = creaArchivioStrumentiFake();

test("test 1", () => { /* aggiunge strumenti */ });
test("test 2", () => { /* vede gli strumenti del test 1 */ }); // BUG

// Fai così — ogni test parte pulito
test("test 1", () => {
  const archivio = creaArchivioStrumentiFake();
  // ...
});
test("test 2", () => {
  const archivio = creaArchivioStrumentiFake();
  // ...
});

Passo per passo nel progetto

  1. Crea test/fakes/archivioStrumentiFake.js con le funzioni salva, trovaTutti, trovaPerId, aggiorna.
  2. Crea test/fakes/archivioPrestitiFake.js con le stesse funzioni più trovaAperti.
  3. Scrivi un test che verifica che salva seguito da trovaPerId restituisce lo stesso strumento.
  4. Scrivi un test che verifica che aggiorna cambia lo stato di uno strumento esistente.
  5. Usa i fake nel test di registraPrestito — verifica il caso felice e il caso di errore.
  6. Usa i fake nel test di chiudiPrestito — verifica che un prestito già chiuso non si chiude di nuovo.

Prova di comprensione

  • Qual è la differenza tra un fake repository e un mock?
  • Perché le funzioni di lettura del fake restituiscono copie invece dei riferimenti originali?
  • Come si garantisce che ogni test parta da uno stato pulito?
  • Quale caso d’uso del Registro è più facile da testare con il fake — e perché?

Errori da evitare

  • condividere un’istanza di fake tra più test senza resettarla — i test si influenzano a vicenda;
  • restituire il riferimento diretto all’array interno invece di una copia — il test può corrompere lo stato;
  • aggiungere nel fake funzioni che non esistono nell’adapter reale — il fake diventa incompatibile;
  • usare il fake per testare il mapping o il formato di salvataggio — quelli vanno testati separatamente.

Prodotto da consegnare

I file test/fakes/archivioStrumentiFake.js e test/fakes/archivioPrestitiFake.js, con almeno tre test che usano i fake per verificare comportamenti reali dei casi d’uso del Registro.

Checklist di chiusura

  • il fake implementa lo stesso contratto dell’adapter di localStorage
  • ogni funzione di lettura restituisce una copia, non un riferimento
  • ogni test crea un nuovo fake (nessuna istanza condivisa)
  • il test del caso felice e del caso di errore passano entrambi
  • i test non importano nulla da localStorage, document o window

Risultato atteso

Ho creato archivioStrumentiFake.js e archivioPrestitiFake.js in test/fakes/.
I fake rispettano il contratto degli adapter reali.
I test di registraPrestito e chiudiPrestito usano i fake e passano senza browser.
Ogni test crea un'istanza nuova del fake — nessuno stato condiviso.
Il prossimo passo sarebbe aggiungere un fake per segnalare guasti.