Il testing è un problema di design
19 marzo 2026

La maggior parte degli sviluppatori che conosco ha un rapporto complicato con i test. Sa che dovrebbe scriverli. Si sente in colpa quando non lo fa. Quando li scrive, spesso sono fragili — si rompono durante i refactoring, richiedono configurazioni elaborate e non danno vera fiducia che il sistema funzioni.
La conclusione comune è di essere scarsi nel testing. Il vero problema è quasi sempre il codice.
Il codice non testabile è un sintomo
Quando ti siedi a scrivere un test e la configurazione richiede quaranta righe, o ti ritrovi a fare il mock di mezza dozzina di dipendenze, non è un problema di testing. È il codice che ti sta dicendo qualcosa. La stessa proprietà che rende il codice difficile da testare — accoppiamento stretto, dipendenze nascoste, funzioni che fanno troppe cose — lo rende anche difficile da ragionare, da modificare e da debuggare in produzione.
Testabilità e buon design sono la stessa qualità espressa in modo diverso. Una funzione facile da testare ha un contratto chiaro: dati questi input, produci questo output, senza effetti collaterali invisibili. È anche quello che la rende riutilizzabile, componibile e sicura da modificare.
Cosa cerco davvero
La domanda che mi faccio mentre costruisco è: posso chiamare questa funzione in isolamento? Non posso fare il mock di tutto quello che la circonda — posso chiamarla con input reali e verificare l'output senza mettere in piedi l'intera applicazione?
Se la risposta è no, guardo cosa c'è in mezzo. Di solito è una di queste cose: la funzione accede direttamente a un database o a un servizio esterno, legge da uno stato globale, o mescola orchestrazione e logica. Non sono problemi di testing. Sono problemi di design. Separare la logica dall'infrastruttura — anche solo in parte — migliora quasi sempre entrambe.
La trappola dei mock
Esiste una versione del testing che dà alta copertura e bassa fiducia. Si fa il mock del database, del client HTTP, del file system. I test passano in millisecondi. Poi qualcosa si rompe in produzione e i test non lo intercettano, perché non stavano mai testando la cosa reale.
È in parte per questo che ho abbandonato Mongoose a favore del driver nativo di MongoDB. Mongoose aggiunge abbastanza astrazione da farti sentire in obbligo di mockarlo. Con il driver nativo e funzioni di query tipizzate, il codice è abbastanza diretto da rendere l'accesso a un database di test reale — anche in CI — più semplice che costruire un mock convincente. I test sono più lenti. Sono anche effettivamente utili.
Dove i test guadagnano il loro posto
Non testo tutto. Per i progetti da solo in particolare, il costo di una suite di test completa può superare il beneficio. Quello che testo:
Le giunture tra sistemi. Endpoint API, query al database, handler di webhook — i punti in cui il mio codice parla con qualcosa al di fuori di sé. È lì che le integrazioni si rompono e i bug si nascondono.
La logica pura con complessità reale. Calcoli di prezzo, aritmetica sulle date, regole di permesso. Facili da testare, veloci da eseguire, e davvero utili perché la logica è il punto.
Le cose che ho già rotto. Quando un bug arriva in produzione, scrivo un test che lo riproduce prima di correggerlo. Il test è economico da scrivere in quel momento, e garantisce che il bug rimanga corretto.
Il testing come feedback
La cosa più utile dei test non è la rete di sicurezza — è il ciclo di feedback. Quando un test è difficile da scrivere, di solito è perché il codice merita di essere più semplice. Quando un test richiede una configurazione complicata, probabilmente la funzione sta facendo troppo.
Se ti ritrovi a evitare i test perché sono dolorosi da scrivere, inizia da lì. Non con un framework di testing o un obiettivo di copertura — con la domanda: perché è difficile chiamare questo in isolamento? La risposta vale la pena di essere affrontata a prescindere dal fatto che tu scriva poi il test.