Glossario · Fondamenti del testing
Unit test (test unitario)
In inglese: Unit test
Detto anche: test unitario, test unitari, unit testing, test di unità
Definizione
Test automatico che verifica una piccola porzione di codice, come una funzione o una classe, in isolamento da database, rete e altri servizi.
Uno unit test (test unitario) mette alla prova una piccola unità di codice, di solito una funzione, un metodo o una classe, e verifica il suo output o comportamento per determinati input. Gira in memoria, senza database reali, chiamate HTTP o file system, quindi impiega millisecondi e fallisce per un solo motivo preciso. Gli unit test formano la base della piramide dei test e di solito vengono scritti dagli sviluppatori insieme al codice.
Esempio (JUnit 5, Java 21)
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
class DiscountCalculatorTest {
private final DiscountCalculator calculator = new DiscountCalculator();
@ParameterizedTest
@CsvSource({ "99.99, 0.00", "100.00, 10.00", "250.00, 25.00" })
void appliesTenPercentFromOneHundred(double total, double expected) {
assertEquals(expected, calculator.discountFor(total), 0.001);
}
@Test
void rejectsNegativeTotals() {
assertThrows(IllegalArgumentException.class, () -> calculator.discountFor(-1));
}
}
I test parametrizzati richiedono l’artifact junit-jupiter-params, incluso nella dipendenza aggregata junit-jupiter. (Il codice che gestisce denaro reale dovrebbe usare BigDecimal invece di double; qui è tenuto semplice.)
Cosa rende buono uno unit test
- Veloce e deterministico. Niente sleep, orologi, casualità o rete. Inietta un
Clockfisso invece di chiamareLocalDate.now(). - Isolato. Le dipendenze vengono sostituite con test double quando renderebbero il test lento o imprevedibile.
- Un comportamento per test, con un nome che lo dichiara, così un fallimento si spiega da solo.
- Testa il comportamento, non l’implementazione. Verifica output ed effetti osservabili, non metodi privati, così il refactoring non rompe i test.
Unit test e test di integrazione
Uno unit test verifica la tua logica con tutto il resto sostituito. Un test di integrazione verifica che componenti reali funzionino insieme: il tuo repository con un database reale, il tuo client con un servizio reale (o in container). Gli unit test intercettano gli errori di logica a basso costo; i test di integrazione intercettano errori di collegamento, configurazione, serializzazione e query che negli unit test spariscono dietro ai mock. Una suite fatta solo di unit test può essere tutta verde mentre l’applicazione non riesce nemmeno ad avviarsi.
Come si definisce una “unità”
Non esiste una risposta universale. Alcuni team considerano unità una singola classe e mockano tutti i collaboratori. Altri considerano unità un piccolo gruppo di classi che implementa un comportamento e sostituiscono solo ciò che attraversa un confine di processo. Il secondo stile tende a produrre test meno fragili durante il refactoring.