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 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.
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.
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.
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.
@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).
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.
| Annotazione | Scopo | Esecuzione |
|---|---|---|
| @Test | Marca un metodo di test | Una volta per chiamata |
| @BeforeEach | Preparazione prima di ogni test | Prima di ogni @Test |
| @BeforeAll | Inizializzazione una tantum della classe | Una volta prima di tutti i test |
| @AfterEach | Pulizia dopo ogni test | Dopo ogni @Test |
| @AfterAll | Completamento una tantum della classe | Una volta dopo tutti i test |
| @DisplayName | Nome del test leggibile | Decorazione |
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.
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();
}
}
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.
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.
@Test
void divisionByZeroShouldThrowException() {
Calculator calc = new Calculator();
ArithmeticException exception = assertThrows(
ArithmeticException.class,
() -> calc.divide(10, 0)
);
assertEquals("Cannot divide by zero", exception.getMessage());
}
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.
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.
@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);
}
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.
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.
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.
// 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")
}
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.
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.
| Caratteristica | JUnit 4 | JUnit 5 |
|---|---|---|
| Pacchetto | org.junit | org.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 min | Java 5 | Java 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
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.
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.
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.
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.
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
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.
Leggi anche