modulo 13 di 24 / Ore 21-40
Moduli 9-16 / Checkpoint 40 ore
Micro-sfida: scrivi un test prima del refactoring
Esercizio guidato: scegliere una funzione del Registro, scrivere il test prima di refactorare, eseguire il refactoring e verificare che il test passi ancora.
Micro-sfida: scrivi un test prima del refactoring
Questa lezione appartiene al Modulo 13 — Testare l’architettura.
Obiettivo della lezione
Completare il ciclo completo: scegliere una funzione del Registro che vuoi migliorare, scrivere un test che descrive il suo comportamento attuale, fare il refactoring, e verificare che il test passi ancora.
Alla fine hai la prova che il refactoring non ha rotto nulla.
Perché serve nel percorso
Il refactoring senza test è una promessa: “ho cambiato la struttura ma il comportamento è lo stesso”. Il test la trasforma in una verifica. Se il test passa prima e dopo, la promessa è mantenuta.
Nel Registro del laboratorio
Scegli una funzione concreta. In questa lezione usiamo cercaStrumenti — una funzione che spesso contiene troppa logica in un posto solo:
// src/application/cercaStrumenti.js — PRIMA del refactoring (funzione lunga)
export function cercaStrumenti({ nome, categoria, stato }, archivio) {
const tutti = archivio.trovaTutti();
return tutti.filter(s => {
let ok = true;
if (nome && nome.trim()) {
if (!s.nome.toLowerCase().includes(nome.trim().toLowerCase())) ok = false;
}
if (categoria && categoria !== "tutte") {
if (s.categoria !== categoria) ok = false;
}
if (stato && stato !== "tutti") {
if (s.stato !== stato) ok = false;
}
return ok;
});
}
Procedura guidata
Passo 1 — Scrivi i test PRIMA di toccare il codice:
// test/application/cercaStrumenti.test.js
import { creaArchivioStrumentiFake } from "../fakes/archivioStrumentiFake.js";
import { cercaStrumenti } from "../../src/application/cercaStrumenti.js";
const strumentiDiTest = [
{ id: "s1", nome: "Oscilloscopio", categoria: "Misura", stato: "disponibile" },
{ id: "s2", nome: "Trapano", categoria: "Utensile", stato: "guasto" },
{ id: "s3", nome: "Oscillatore", categoria: "Misura", stato: "in_prestito" }
];
test("restituisce tutti gli strumenti senza filtri", () => {
const archivio = creaArchivioStrumentiFake(strumentiDiTest);
const risultato = cercaStrumenti({}, archivio);
expect(risultato).toHaveLength(3);
});
test("filtra per nome (case-insensitive, parziale)", () => {
const archivio = creaArchivioStrumentiFake(strumentiDiTest);
const risultato = cercaStrumenti({ nome: "oscillo" }, archivio);
expect(risultato).toHaveLength(2); // Oscilloscopio e Oscillatore
});
test("filtra per categoria", () => {
const archivio = creaArchivioStrumentiFake(strumentiDiTest);
const risultato = cercaStrumenti({ categoria: "Misura" }, archivio);
expect(risultato).toHaveLength(2);
});
test("filtra per stato", () => {
const archivio = creaArchivioStrumentiFake(strumentiDiTest);
const risultato = cercaStrumenti({ stato: "guasto" }, archivio);
expect(risultato).toHaveLength(1);
expect(risultato[0].id).toBe("s2");
});
test("combina nome e categoria", () => {
const archivio = creaArchivioStrumentiFake(strumentiDiTest);
const risultato = cercaStrumenti({ nome: "oscillo", categoria: "Misura" }, archivio);
expect(risultato).toHaveLength(2);
});
Passo 2 — Verifica che i test passino con il codice originale:
Esegui i test. Devono passare tutti. Se non passano, il codice originale ha un bug — correggilo prima di procedere.
Passo 3 — Fai il refactoring:
// src/application/cercaStrumenti.js — DOPO il refactoring
export function cercaStrumenti({ nome, categoria, stato } = {}, archivio) {
const tutti = archivio.trovaTutti();
return tutti
.filter(s => corrispondeAlNome(s, nome))
.filter(s => corrispondeAllaCategoria(s, categoria))
.filter(s => corrispondeAlloStato(s, stato));
}
function corrispondeAlNome(strumento, nome) {
if (!nome || !nome.trim()) return true;
return strumento.nome.toLowerCase().includes(nome.trim().toLowerCase());
}
function corrispondeAllaCategoria(strumento, categoria) {
if (!categoria || categoria === "tutte") return true;
return strumento.categoria === categoria;
}
function corrispondeAlloStato(strumento, stato) {
if (!stato || stato === "tutti") return true;
return strumento.stato === stato;
}
Passo 4 — Verifica che i test passino ancora:
Esegui di nuovo gli stessi test. Devono passare tutti. Il refactoring è sicuro.
Attività in classe
Scegli tu la funzione da refactorare — può essere registraStrumento, chiudiPrestito o apriSegnalazioneGuasto. Segui i quattro passi:
## Micro-sfida: test prima del refactoring
Funzione scelta:
Test scritti prima del refactoring:
Risultato del primo run (tutti passano?):
Refactoring eseguito (descrizione breve):
Risultato del secondo run (tutti passano ancora?):
Cosa è cambiato nella struttura:
Cosa NON è cambiato nel comportamento:
Spiegazione guidata per studiare
Il ciclo corretto del refactoring è:
- Scrivi i test che descrivono il comportamento attuale.
- Verifica che passino (il codice funziona come pensi).
- Fai il refactoring.
- Verifica che i test passino ancora (il comportamento non è cambiato).
Se salti il passo 1, il passo 4 non ha senso — non hai nulla da confrontare.
Cosa testare: non testare l’implementazione interna (come è scritto il codice), testa il comportamento osservabile (cosa restituisce, quali errori lancia). Se il test dipende da dettagli interni, diventa fragile e si rompe ad ogni refactoring.
Esempio di test fragile (da evitare):
// FRAGILE: testa l'implementazione interna
test("chiama trovaTutti una sola volta", () => {
const archivio = { trovaTutti: jest.fn(() => []) };
cercaStrumenti({}, archivio);
expect(archivio.trovaTutti).toHaveBeenCalledTimes(1); // dipende da come è scritto
});
Esempio di test robusto:
// ROBUSTO: testa il comportamento
test("restituisce lista vuota se nessuno strumento corrisponde", () => {
const archivio = creaArchivioStrumentiFake([
{ id: "s1", nome: "Trapano", categoria: "Utensile", stato: "disponibile" }
]);
const risultato = cercaStrumenti({ categoria: "Misura" }, archivio);
expect(risultato).toHaveLength(0);
});
Passo per passo nel progetto
- Scegli una funzione in
src/application/da refactorare. - Crea (o apri) il file di test corrispondente in
test/application/. - Scrivi almeno 3 test che coprono: caso felice, filtro specifico, caso limite (lista vuota).
- Esegui i test e verifica che passino.
- Fai il refactoring (estrai funzioni, semplifica condizioni, rimuovi duplicazioni).
- Esegui di nuovo i test — devono passare tutti.
- Documenta nel commit: “refactoring di X — comportamento invariato, test passano”.
Prova di comprensione
- Perché i test vanno scritti prima del refactoring e non dopo?
- Cosa significa che un test è “fragile”?
- Se un test fallisce dopo il refactoring, come sai se il problema è nel test o nel codice?
- Quanti test sono sufficienti prima di un refactoring?
Errori da evitare
- scrivere i test dopo il refactoring — non dimostrano nulla sul comportamento originale;
- testare l’implementazione interna invece del comportamento — i test diventano ostacoli;
- fare il refactoring e i test in un unico commit — non si capisce cosa ha rotto cosa;
- saltare il run dei test dopo il refactoring — il punto centrale della micro-sfida.
Prodotto da consegnare
I test scritti prima del refactoring (con il run iniziale che passa), il codice refactorizzato e il run finale che passa. Due commit separati: uno con i test, uno con il refactoring.
Checklist di chiusura
- i test sono stati scritti prima del refactoring
- il primo run dei test passa (il codice originale funziona come atteso)
- il refactoring è stato fatto in un commit separato
- il secondo run dei test passa (il comportamento non è cambiato)
- i test coprono almeno il caso felice e un caso limite
Risultato atteso
Ho scelto cercaStrumenti come funzione da refactorare.
Ho scritto 5 test prima del refactoring — tutti passano.
Ho estratto le funzioni di filtro (corrispondeAlNome, corrispondeAllaCategoria, corrispondeAlloStato).
Il secondo run dei test passa ancora — tutti e 5.
Il comportamento è invariato, la struttura è più leggibile.