🕐 Tempo di lettura: 3 minuti

Vedere la percentuale di code coverage che sale ci fa dormire sereni. Ma il nostro sistema è davvero testato o lo è solo apparentemente?

📌 La branch coverage

Osserviamo lo screenshot qui sotto, in particolare la riga dove viene invocato il Predicate isNotStringOrNotBlankString. Abbiamo true hits: 161, false hits: 0.

Screenshot di IntelliJ con la finestra di branch coverage: il predicato isNotStringOrNotBlankString.test(value) mostra true hits 161 e false hits 0 su 333 hit totali

Cosa ci dice quello zero? Che, nonostante i 333 passaggi totali, non abbiamo mai attraversato il ramo in cui quel predicato fallisce. Questo è sintomo solitamente di una delle seguenti due cose:

La branch coverage è il miglior "potatore" di codice inutile che si possa avere!

📌 La trappola combinatoria

Arriviamo al secondo punto: testare solo tramite integration test è un tentativo di risolvere un problema di calcolo combinatorio con la forza bruta.

Immaginiamo un sistema con L layer e M metodi, dove ogni metodo ha delle diramazioni logiche (branch). Se proviamo a testare tutto tramite integrazione, le combinazioni di input che devono attraversare i vari layer crescono in modo esponenziale. Testare ogni scenario possibile provando ogni singola strada partendo sempre dalla porta principale è computazionalmente irrealistico.

📌 I test di unità sono il "cheat code"

Hey, non fraintendetemi, non dico che i test d'integrazione non servano: sono quelli che ci dicono che il sistema gira realmente in condizioni realistiche. Ma sono i test di unità a validare la logica e dirci se ogni pezzetto funziona davvero. Ignorare la branch coverage e affidarsi solo a flussi end-to-end significa guardare il sistema da lontano, dove apparentemente tutto funziona, ma dando per scontato quel che succede dentro.

Alla prossima pillola! ☕