W3docs

Annotazioni JUnit in Java

Annotazioni principali di JUnit 5 — @Test, @BeforeEach, @AfterEach, @BeforeAll, @AfterAll, @Disabled.

JUnit 5 è il framework di test standard de facto per Java, e quasi tutto ciò che gli si comunica avviene tramite annotazioni. Non si scrive un metodo main né si chiamano i metodi manualmente; si decorano i metodi ordinari con annotazioni come @Test, @BeforeEach e @AfterAll, e il motore JUnit li individua tramite riflessione e li esegue nell'ordine corretto. Questo capitolo tratta le annotazioni principali del ciclo di vita — cosa significa ciascuna, quando viene attivata e come si combinano per garantire a ogni test una fixture pulita e isolata.

Se sei nuovo al framework, inizia con l'introduzione a JUnit; per gli helper di asserzione che questi test utilizzano, consulta le asserzioni JUnit. Le annotazioni stesse sono una funzionalità generale di Java trattata in le annotazioni Java.

Le annotazioni si trovano in org.junit.jupiter.api

L'API di JUnit 5 è il modulo Jupiter. Le annotazioni che si usano quotidianamente provengono tutte da un unico package:

AnnotazioneSi applica aViene eseguita
@Testun metodouna volta per ogni metodo di test
@BeforeEachun metodoprima di ogni @Test
@AfterEachun metododopo ogni @Test
@BeforeAllun metodo staticuna volta, prima di qualsiasi test nella classe
@AfterAllun metodo staticuna volta, dopo tutti i test nella classe
@Disabledun metodo o una classemai (viene saltato e segnalato)
@DisplayNameun metodo o una classeimposta un nome leggibile nei report

Un metodo contrassegnato con @Test non necessita del modificatore public in JUnit 5 (package-private va bene) e deve restituire void.

@Test: l'unità di lavoro

Un metodo di test verifica qualcosa tramite gli helper statici in org.junit.jupiter.api.Assertions. Se un'asserzione fallisce viene lanciata un'eccezione, e il motore registra quel singolo test come fallito senza interrompere gli altri.

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class CalculatorTest {

    @Test
    void addsTwoNumbers() {
        Calculator calc = new Calculator();
        assertEquals(5, calc.add(2, 3));
    }

    @Test
    void throwsOnDivideByZero() {
        Calculator calc = new Calculator();
        assertThrows(ArithmeticException.class, () -> calc.divide(1, 0));
    }
}

Ogni @Test viene eseguito su una nuova istanza della classe di test — JUnit costruisce un nuovo oggetto per ogni test per impostazione predefinita, quindi i campi impostati in un test non possono influenzare un altro.

@BeforeEach e @AfterEach: fixture per test

La configurazione di cui ogni test ha bisogno va in un metodo @BeforeEach; il teardown va in @AfterEach. Questi metodi racchiudono ogni @Test, fornendo a ogni test un punto di partenza identico.

import org.junit.jupiter.api.*;

class OrderServiceTest {

    private OrderService service;

    @BeforeEach
    void setUp() {
        service = new OrderService(new InMemoryRepo()); // fresh state per test
    }

    @AfterEach
    void tearDown() {
        service.close(); // runs even if the test threw
    }

    @Test
    void placesOrder() {
        assertTrue(service.place("SKU-1", 2));
    }
}

@AfterEach viene eseguito anche quando il test fallisce, il che lo rende il posto giusto per rilasciare le risorse aperte in @BeforeEach.

@BeforeAll e @AfterAll: una volta per classe

Quando la configurazione è costosa e condivisibile — un container di database, un server embedded avviato — si usa @BeforeAll per eseguirla una volta e @AfterAll per smantellarlo una volta. Poiché vengono eseguiti prima che esista qualsiasi istanza, devono essere static.

import org.junit.jupiter.api.*;

class RepositoryTest {

    static Database db;

    @BeforeAll
    static void startDatabase() {
        db = Database.start();   // runs once, before everything
    }

    @AfterAll
    static void stopDatabase() {
        db.stop();               // runs once, after everything
    }

    @Test
    void savesRow() {
        assertEquals(1, db.insert("hello"));
    }
}

L'ordine completo del ciclo di vita per una classe con due test è: @BeforeAll → (@BeforeEach@Test@AfterEach) → (@BeforeEach@Test@AfterEach) → @AfterAll. Il capitolo sul ciclo di vita JUnit approfondisce questo ordinamento, incluso come interagisce con la creazione delle istanze.

@Disabled: saltare senza eliminare

@Disabled disattiva un test (o un'intera classe). Il motore lo segnala come saltato anziché passato o fallito, in modo che rimanga visibile. Fornire sempre un motivo.

@Test
@Disabled("flaky until the rate-limiter fix lands — see JIRA-1234")
void callsExternalApi() {
    // not executed
}

Un esempio pratico: un mini motore di test

Non c'è nessun jar JUnit in questo runner, quindi il programma seguente costruisce un piccolo motore proprio con la stessa struttura di JUnit. Dichiara annotazioni marcatori (@BeforeAll, @BeforeEach, @Test, @AfterEach, @AfterAll, @Disabled), definisce una piccola classe di test annotata, quindi usa la riflessione — esattamente ciò che fa internamente il motore di JUnit — per individuare ed eseguire i metodi nell'ordine del ciclo di vita e stampare un riepilogo pass/fail/skip.

java— editable, runs on the server

Cosa osservare dall'esecuzione:

  • @BeforeAll viene stampato esattamente una volta all'inizio e @AfterAll esattamente una volta alla fine — la configurazione e il teardown a livello di classe racchiudono l'intera esecuzione, motivo per cui JUnit richiede che siano static.
  • Ogni @Test eseguito è preceduto da una riga @BeforeEach e seguito da una riga @AfterEach, quindi ogni test è stato eseguito su una fixture preparata di fresco e ha fatto pulizia dopo di sé — il bracketing per-test che mantiene i test indipendenti.
  • Il metodo flaky portava @Disabled, quindi ha stampato (skipped via @Disabled) e il suo corpo non è mai stato eseguito; l'asserzione fallente al suo interno non è mai stata raggiunta, che è proprio il punto di disabilitare anziché eliminare.
  • Il ciclo di scoperta agisce solo sui metodi dove isAnnotationPresent(Test.class) è vero — le annotazioni sono solo metadati, ed è il motore che le legge tramite riflessione a trasformarle in comportamento, esattamente come funziona il vero JUnit.
  • La riga finale riporta 2 passed, 0 failed, 1 skipped: due test reali sono passati, nessuno è fallito, e quello disabilitato è stato conteggiato come saltato anziché ignorato silenziosamente — la stessa contabilità pass/fail/skip che fornisce un report JUnit.

Pratica

Pratica
In JUnit 5, perche un metodo annotato con @BeforeAll deve essere dichiarato static?
In JUnit 5, perche un metodo annotato con @BeforeAll deve essere dichiarato static?
Was this page helpful?