JUnit: cos'è, concetti chiave e come funziona

Autore: IT Sectr Pubblicato: 2026-04-08 Tempo di lettura: 8 min

JUnit è un framework standard per i test unitari nell'ecosistema Java e Kotlin, utilizzato nello sviluppo Android per verificare la logica di business a livello di componenti isolati. Il framework fornisce un insieme di annotazioni, metodi di asserzione e un Test Runner per il rilevamento e l'esecuzione automatici dei test. Secondo JUnit.org, la libreria rimane la soluzione più popolare per i test unitari nell'ecosistema JVM: più del 70% dei progetti Java utilizza JUnit in una qualsiasi versione.

Punti chiave

  • JUnit è un framework open-source per test unitari di codice Java e Kotlin.
  • Annotazioni — @Test, @BeforeEach, @BeforeAll gestiscono il ciclo di vita della classe di test.
  • Asserzioni — assertEquals, assertTrue, assertThrows verificano i risultati attesi dell'esecuzione.
  • Test parametrizzati — consentono di eseguire un singolo test con diversi set di dati di input.
  • Test Runner — l'infrastruttura JUnit trova ed esegue automaticamente tutti i test nel progetto.

Cos'è JUnit?

JUnit è un framework open-source per scrivere ed eseguire test unitari ripetibili in Java e Kotlin. Fa parte dell'ecosistema xUnit, una famiglia di framework basati sull'architettura di Kent Beck e Erich Gamma, ed è lo strumento di test standard in Android Studio e IntelliJ IDEA.

Il compito principale di JUnit è isolare un piccolo frammento di codice (metodo, classe) e verificarne il comportamento in condizioni controllate. I test vengono scritti come classi Java/Kotlin ordinarie con annotazioni, e il framework si occupa del rilevamento dei test, della gestione del ciclo di vita e della raccolta delle statistiche di esecuzione.

La prima versione di JUnit è apparsa nel 1997 e ha cambiato radicalmente l'approccio allo sviluppo, rendendo popolari le pratiche di Test-Driven Development (TDD). Oggi, JUnit 5 (Jupiter) è la versione attuale, completamente riprogettata con un'architettura modulare che supporta Java 8+ e le estensioni.

Come funziona JUnit

Il meccanismo di esecuzione di JUnit si basa sul pattern Test Runner, che scansiona il classpath, trova i metodi con l'annotazione @Test, crea un'istanza della classe di test ed esegue i metodi in un ordine specifico. Il Test Runner gestisce il ciclo di vita: BeforeAll → BeforeEach → Test → AfterEach → AfterAll.

Ciclo di vita della classe di test

JUnit incapsula ogni metodo di test in un'istanza di classe separata, garantendo l'isolamento tra i test. Ciò significa che i campi della classe non mantengono lo stato tra diversi metodi @Test — ogni test inizia con un oggetto pulito.

Struttura base del test

Il test più semplice in JUnit è un metodo con l'annotazione @Test che chiama il codice sotto test e verifica il risultato utilizzando un'asserzione. Se l'asserzione fallisce, il test è considerato fallito e JUnit segnala l'errore.

java
@Test
void additionShouldReturnCorrectSum() {
    var calculator = new Calculator();
    var result = calculator.add(2, 3);
    assertEquals(5, result);
}

Il Test Runner trova automaticamente tale metodo, lo esegue e segnala il risultato. Se il metodo non lancia un'eccezione — il test supera (green). Se un'asserzione fallisce — il test fallisce (red).

Annotazioni JUnit: ciclo di vita del test

Le annotazioni in JUnit definiscono quando e come il codice di test deve essere eseguito. In JUnit 5, le annotazioni si trovano nel pacchetto org.junit.jupiter.api e coprono tutte le fasi: preparazione dei dati, esecuzione del test, pulizia delle risorse.

AnnotazioneScopoEsecuzione
@TestMarca un metodo di testUna volta per chiamata
@BeforeEachPreparazione prima di ogni testPrima di ogni @Test
@BeforeAllInizializzazione una tantum della classeUna volta prima di tutti i test
@AfterEachPulizia dopo ogni testDopo ogni @Test
@AfterAllCompletamento una tantum della classeUna volta dopo tutti i test
@DisplayNameNome del test leggibileDecorazione

Esempio di utilizzo delle annotazioni

Consideriamo una classe di test completa con un corretto ciclo di vita. Il metodo setUp crea una nuova istanza di Calculator prima di ogni test, e tearDown rilascia le risorse — ad esempio, chiudendo descrittori di file o connessioni al database.

java
class CalculatorTest {

    private Calculator calculator;

    @BeforeEach
    void setUp() {
        calculator = new Calculator();
    }

    @Test
    void subtractionShouldReturnCorrectResult() {
        int result = calculator.subtract(10, 4);
        assertEquals(6, result);
    }

    @AfterEach
    void tearDown() {
        calculator.reset();
    }
}

Asserzioni: verifica dei risultati in JUnit

Le asserzioni sono metodi statici che confrontano il risultato effettivo con quello atteso e lanciano un'eccezione in caso di mancata corrispondenza. JUnit 5 offre la classe org.junit.jupiter.api.Assertions con oltre 25 metodi per diversi scenari di verifica — dal semplice confronto di valori ai controlli di timeout e alle asserzioni raggruppate.

  • assertEquals(expected, actual) — verifica l'uguaglianza tramite equals().
  • assertTrue(condition) — verifica che una condizione sia vera.
  • assertFalse(condition) — verifica che una condizione sia falsa.
  • assertNotNull(object) — verifica che un oggetto non sia null.
  • assertNull(object) — verifica che un oggetto sia null.
  • assertThrows(exception, executable) — verifica che il codice lanci un'eccezione specifica.
  • assertAll(executables) — raggruppa più asserzioni e le esegue tutte senza fermarsi al primo fallimento.

assertThrows per la verifica delle eccezioni

Particolare attenzione merita il metodo assertThrows, che consente di verificare che il codice gestisca correttamente le situazioni di errore — divisione per zero, passaggio di null, superamento dei limiti.

java
@Test
void divisionByZeroShouldThrowException() {
    Calculator calc = new Calculator();

    ArithmeticException exception = assertThrows(
        ArithmeticException.class,
        () -> calc.divide(10, 0)
    );

    assertEquals("Cannot divide by zero", exception.getMessage());
}

Test parametrizzati in JUnit

I test parametrizzati consentono di eseguire lo stesso metodo di test con diversi set di argomenti. In JUnit 5, ciò viene fatto utilizzando l'annotazione @ParameterizedTest combinata con una fonte di dati — @ValueSource, @CsvSource, @MethodSource o @EnumSource.

Questo approccio riduce radicalmente la duplicazione del codice: invece di dieci test identici per valori diversi, si scrive un singolo metodo parametrizzato. Secondo il Google Testing Blog, la parametrizzazione riduce il volume del codice di test del 40–60% senza perdere copertura.

Esempio con CsvSource

L'annotazione @CsvSource passa più righe separate da virgole al test. Ogni riga corrisponde a un'esecuzione del test. JUnit converte automaticamente i valori stringa nei tipi richiesti: int, long, String e altri.

java
@ParameterizedTest
@CsvSource({
    "1,    1,   2",
    "2,    3,   5",
    "10,  20,  30",
    "-1,   1,   0"
})
void additionWithMultipleInputs(int a, int b, int expected) {
    assertEquals(expected, a + b);
}

MethodSource per oggetti complessi

Quando i dati di input sono più complessi di semplici numeri o stringhe, si utilizza @MethodSource. Fa riferimento a un metodo statico che restituisce uno Stream di argomenti — ad esempio, uno Stream di Arguments o uno Stream di oggetti User per testare il livello DAO.

Integrazione di JUnit con progetti Android

Nello sviluppo Android, JUnit viene utilizzato insieme ad AndroidX Test, che fornisce estensioni per testare Activity, Content Provider e altri componenti Android. JUnit gestisce i test unitari eseguiti sulla JVM senza emulatore, mentre AndroidX Test gestisce i test strumentati sul dispositivo.

Configurazione di Gradle

Per aggiungere JUnit 5 a un progetto Android, è sufficiente includere la dipendenza org.junit.jupiter:junit-jupiter nel build.gradle a livello di modulo. Il plugin AGP di Gradle supporta l'esecuzione di test JUnit 5 sulla JVM tramite il Test Runner standard.

kotlin
// build.gradle.kts
android {
    testOptions {
        unitTests.isIncludeAndroidResources = true
    }
}

dependencies {
    testImplementation("org.junit.jupiter:junit-jupiter:5.11.0")
    testImplementation("androidx.test:core-ktx:1.6.1")
}

Test di ViewModel con JUnit

In pratica, JUnit viene utilizzato più spesso per testare ViewModel e repository — livelli che non richiedono contesto Android né emulatore. Un tale test viene eseguito in millisecondi e può essere eseguito centinaia di volte senza perdere tempo.

  • ViewModel — verifica degli stati: loading, success, error.
  • Repository — verifica del mapping dei dati e della gestione degli errori.
  • UseCase — verifica della logica di business con mock delle dipendenze.

JUnit 4 vs JUnit 5: differenze principali

JUnit 5 (Jupiter) non è solo una nuova versione, ma una piattaforma completamente riprogettata divisa in tre moduli: JUnit Platform (esecuzione dei test sulla JVM), JUnit Jupiter (API per scrivere test) e JUnit Vintage (retrocompatibilità con JUnit 4). Questa modularità consente di collegare diversi Test Engine — ad esempio, Spek per Kotlin o TestNG.

CaratteristicaJUnit 4JUnit 5
Pacchettoorg.junitorg.junit.jupiter
Annotazione base@Test (da junit.framework)@Test (da org.junit.jupiter.api)
Before/After@Before, @After, @BeforeClass@BeforeEach, @AfterEach, @BeforeAll
Parametrizzati@RunWith(Parameterized.class)@ParameterizedTest + @ValueSource
Estensione@Rule, @ClassRule@ExtendWith, API più flessibile
Java minJava 5Java 8+

Migrare da JUnit 4 a JUnit 5 non richiede la riscrittura di tutti i test — basta aggiungere JUnit Vintage Engine e i vecchi test continueranno a funzionare. Si consiglia di scrivere i nuovi test con JUnit 5 per sfruttare le estensioni, le asserzioni lambda e il supporto integrato per la parametrizzazione.

Domande frequenti

In cosa si differenzia JUnit da Mockito?

JUnit è un framework per scrivere ed eseguire test, mentre Mockito è una libreria per creare oggetti mock. Vengono utilizzati insieme: JUnit gestisce l'esecuzione del test e Mockito sostituisce le dipendenze della classe sotto test.

Si può usare JUnit 5 nei progetti Android?

Sì, JUnit 5 è completamente compatibile con i progetti Android. Per i test unitari, basta aggiungere la dipendenza junit-jupiter in build.gradle. I test strumentati continuano a funzionare tramite AndroidX Test Runner.

Qual è il set minimo di annotazioni necessario per un test?

Come minimo, una sola annotazione @Test prima del metodo è sufficiente. Per l'inizializzazione e la pulizia sono raccomandati @BeforeEach e @AfterEach, ma non sono obbligatori.

Cos'è il Test Runner in JUnit?

Il Test Runner è un componente di JUnit che scansiona il classpath, trova i metodi con l'annotazione @Test, crea istanze delle classi di test ed esegue i test. In JUnit 5, questo ruolo è svolto da JUnit Platform con i Test Engine collegati.

Come verificare che un metodo lanci un'eccezione?

Utilizzare assertThrows(Class, Executable) — accetta il tipo di eccezione atteso e una lambda con la chiamata al metodo. JUnit verifica che l'eccezione sia stata effettivamente lanciata e la restituisce per ulteriori ispezioni.

Riepilogo

  • JUnit è il framework standard per i test unitari di Java e Kotlin, fondamento dell'ecosistema xUnit.
  • Annotazioni @Test, @BeforeEach, @AfterEach gestiscono il ciclo di vita della classe di test.
  • Asserzioni — assertEquals, assertTrue, assertThrows verificano la correttezza dei risultati.
  • Test parametrizzati con @CsvSource e @MethodSource riducono la duplicazione del codice.
  • JUnit 5 — architettura modulare con piattaforma, API Jupiter e supporto alle estensioni.
  • Integrazione Android — JUnit viene eseguito sulla JVM tramite il Test Runner standard senza emulatore.
  • Test Runner rileva ed esegue automaticamente i metodi @Test, riportando i risultati.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche