{ } Programmazione

La piramide dei test: unitari, integrazione ed end-to-end

Cos'è la piramide dei test e come bilanciare test unitari, di integrazione ed end-to-end per avere una suite veloce, affidabile e davvero utile al team.

Una volta capito il valore dei test automatici, la domanda successiva è inevitabile: quali test scrivere, e in che proporzione? Un'applicazione può essere verificata a livelli molto diversi, dalla singola funzione fino all'utente simulato che clicca nel browser. Ogni livello ha costi e benefici diversi. La piramide dei test è il modello più noto per ragionare su questo equilibrio.

Il modello

L'immagine della piramide, resa popolare da Mike Cohn, divide i test in tre livelli:

  1. Alla base, molti test unitari: verificano singole funzioni o classi, sono veloci ed economici.
  2. Al centro, un numero moderato di test di integrazione: verificano che più componenti funzionino insieme, per esempio il codice e il database.
  3. In cima, pochi test end-to-end: simulano un utente reale che attraversa l'intera applicazione.

La forma esprime un'idea semplice: più si sale, più i test diventano lenti, costosi da mantenere e difficili da diagnosticare, quindi conviene averne meno.

Confronto tra i livelli

Caratteristica Unitari Integrazione End-to-end
Velocità Millisecondi Secondi o frazioni Secondi o minuti
Cosa verificano Logica isolata Collaborazione tra parti Flusso completo dell'utente
Precisione della diagnosi Alta Media Bassa
Stabilità Alta Media Più fragili
Fiducia sul sistema reale Limitata Buona Alta

La tabella mostra perché non esiste un livello migliore in assoluto. I test unitari dicono esattamente dove si trova un errore, ma non garantiscono che i pezzi si incastrino. I test end-to-end danno molta fiducia, ma quando falliscono può essere difficile capire il perché.

I test di integrazione

Il livello intermedio è spesso quello più trascurato, eppure intercetta una categoria di errori molto frequente: tutto funziona da solo, ma non insieme. Esempi tipici:

  • una query SQL corretta sintatticamente ma che non restituisce i dati attesi con lo schema reale;
  • un servizio che si aspetta date in un formato e ne riceve un altro;
  • una configurazione che manca nell'ambiente di test ma c'è in quello di sviluppo.

Per questi test conviene usare componenti reali in versione controllata: un database vero avviato in un container, con dati preparati per ogni test e ripuliti alla fine. Simulare il database con un mock, in questi casi, rischierebbe di nascondere proprio gli errori che si vogliono trovare.

I test end-to-end

Un test end-to-end per un negozio online potrebbe fare questo: aprire la home page, cercare un prodotto, aggiungerlo al carrello, completare il pagamento con dati di prova e verificare la conferma dell'ordine. Strumenti come Playwright, Cypress o Selenium pilotano un browser vero per eseguire questi passaggi.

Sono preziosi per i percorsi critici, quelli che se si rompono fermano l'attività: registrazione, accesso, acquisto. Ma vanno scelti con parsimonia, per tre motivi:

  1. sono lenti, e una suite che dura un'ora viene eseguita meno spesso;
  2. sono sensibili a dettagli come tempi di caricamento e animazioni, e tendono a fallire a intermittenza;
  3. quando falliscono, la causa può trovarsi in qualunque punto dello stack.

L'anti-pattern del cono gelato

Il problema opposto alla piramide è il cosiddetto "cono gelato": pochissimi test unitari, qualche test di integrazione e una grande quantità di test end-to-end o, peggio, di verifiche manuali. È una situazione comune nei progetti in cui i test sono stati aggiunti a posteriori, partendo da ciò che si vedeva dall'esterno. Il risultato è una suite lenta, instabile e che il team impara a ignorare.

Uscirne richiede pazienza: ogni volta che un test end-to-end individua un bug, conviene chiedersi se lo stesso errore poteva essere intercettato da un test più in basso, e scriverlo.

Altri modelli

La piramide non è un dogma. Alcuni sviluppatori propongono forme diverse, come il "trofeo dei test", che dà più peso ai test di integrazione, soprattutto per applicazioni frontend in cui gran parte della logica sta nell'interazione tra componenti. Il punto comune a tutti i modelli è lo stesso: scegliere consapevolmente dove investire, invece di accumulare test a caso.

Come applicarla in pratica

Alcune indicazioni concrete per un progetto reale:

  • scrivi test unitari per la logica di business: calcoli, regole, validazioni, trasformazioni di dati;
  • scrivi test di integrazione per i confini del sistema: database, code di messaggi, API esterne simulate in modo realistico;
  • scrivi test end-to-end solo per i flussi che non possono assolutamente rompersi;
  • esegui i test unitari a ogni modifica, quelli di integrazione a ogni push, quelli end-to-end almeno prima di ogni rilascio;
  • tratta i test instabili come bug: correggili o rimuovili, non ignorarli.

In sintesi

  • La piramide suggerisce molti test unitari, un numero moderato di test di integrazione e pochi test end-to-end.
  • Più si sale, più i test sono lenti, costosi e difficili da diagnosticare, ma danno più fiducia sul sistema reale.
  • I test di integrazione intercettano gli errori tra componenti e usano preferibilmente risorse reali controllate.
  • Riserva i test end-to-end ai percorsi critici dell'applicazione.
  • Evita il "cono gelato": sposta le verifiche verso il basso ogni volta che puoi.
MM
Marco Marzaduri

Marco Marzaduri si occupa di cybersecurity, informatica forense e sviluppo web. Laureato in informatica e in psicologia, unisce l'analisi tecnica alla comprensione del fattore umano.

Leggi anche