W3docs

Asserzioni JUnit in Java

Verifica il comportamento in JUnit 5 con assertEquals, assertTrue, assertThrows, assertAll e altri metodi.

Un'asserzione è il momento in cui un test smette di descrivere e inizia a verificare — stabilisce cosa il codice dovrebbe produrre e fa fallire il test se la realtà non corrisponde. JUnit 5 (l'API Jupiter) raccoglie ogni asserzione come metodo static su un'unica classe, org.junit.jupiter.api.Assertions. Imparare quella manciata di metodi ti permette di esprimere quasi qualsiasi aspettativa: uguaglianza, verità, nullità, identità, eccezioni lanciate, timeout e controlli raggruppati. Questo capitolo illustra quelli che usi quotidianamente.

Se sei nuovo al framework, leggi prima l'introduzione a JUnit, poi le annotazioni JUnit per capire da dove provengono i metodi @Test. Le asserzioni vivono all'interno di quei metodi.

Il modello mentale: un'asserzione fallita fa fallire il test

Un metodo di test JUnit viene eseguito dall'alto verso il basso. Ogni asserzione soddisfatta è silenziosa; la prima che non viene soddisfatta lancia un AssertionFailedError, che JUnit intercetta e registra come fallimento — il metodo si interrompe lì. Le asserzioni sono quindi i punti di uscita del test. L'ordine convenzionale degli argomenti è valore atteso prima, valore effettivo secondo, e ogni metodo accetta un messaggio finale opzionale usato solo quando il controllo fallisce:

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

class CalculatorTest {
  @Test
  void addsTwoNumbers() {
    int result = 2 + 3;
    assertEquals(5, result, "2 + 3 should equal 5");
  }
}

I metodi vengono importati staticamente (import static ... Assertions.*) in modo che il corpo del test si legga come un testo naturale — assertEquals(...), non Assertions.assertEquals(...).

Uguaglianza, verità e nullità

Questi quattro coprono la grande maggioranza delle asserzioni reali:

MetodoPassa quando
assertEquals(expected, actual)expected.equals(actual) è true
assertNotEquals(unexpected, actual)i due valori non sono uguali
assertTrue(condition) / assertFalse(condition)il boolean è true / false
assertNull(obj) / assertNotNull(obj)il riferimento è / non è null
assertSame(expected, actual)entrambi fanno riferimento allo stesso oggetto (==)
assertArrayEquals(expected, actual)gli array sono uguali elemento per elemento
assertEquals("HELLO", "hello".toUpperCase());
assertTrue(List.of(1, 2, 3).contains(2), "list should contain 2");
assertNull(map.get("missing"));
assertNotSame(new String("a"), new String("a")); // distinct objects
assertArrayEquals(new int[]{1, 2, 3}, computeRange(3));

assertEquals usa .equals(), quindi due oggetti String diversi con gli stessi caratteri passano; assertSame usa ==, quindi falliscono. Usa assertSame solo quando vuoi testare l'identità dell'oggetto.

Testare le eccezioni con assertThrows

Un test a volte verifica che il codice fallisca — che un input non valido lanci un'eccezione. assertThrows riceve il tipo di eccezione e una lambda; passa solo se l'esecuzione della lambda lancia quel tipo (o un sottotipo), e restituisce l'eccezione catturata in modo che tu possa ispezionarne il messaggio:

@Test
void rejectsNullName() {
  IllegalArgumentException ex = assertThrows(
      IllegalArgumentException.class,
      () -> greet(null));
  assertEquals("name must not be null", ex.getMessage());
}

Il suo opposto è assertDoesNotThrow, che fallisce se la lambda lancia qualcosa — utile per verificare che un percorso precedentemente difettoso ora funzioni correttamente. (Per le regole con cui il codice lancia eccezioni, vedi eccezioni Java.)

Raggruppamento con assertAll

Per impostazione predefinita, la prima asserzione fallita termina il metodo, nascondendo eventuali problemi successivi. assertAll esegue ogni asserzione contenuta anche quando alcune falliscono, poi riporta tutti i fallimenti insieme — ideale per controllare più proprietà di un oggetto:

@Test
void buildsCompleteUser() {
  User u = User.of("[email protected]");
  assertAll("user",
      () -> assertEquals("[email protected]", u.email()),
      () -> assertTrue(u.isActive()),
      () -> assertNotNull(u.createdAt()));
}

Se sia l'email che il flag attivo sono errati, un'unica esecuzione te lo dice — invece di correggere uno, rieseguire e scoprire il successivo.

Un esempio pratico: un'esecuzione di test auto-verificante, senza framework

Il code runner non ha JUnit nel suo classpath, quindi questo programma implementa la stessa idea in Java puro: piccoli helper assertEquals, assertTrue, assertThrows e assertAll che si comportano come quelli di Jupiter — silenziosi in caso di successo, espliciti in caso di fallimento — che guidano una serie di metodi sotto test e stampano un riepilogo finale in stile runner. L'API che scriveresti effettivamente si trova nei blocchi statici sopra; questo mostra cosa fanno quei metodi.

java— editable, runs on the server

Cosa ricavare dall'esecuzione:

  • I controlli superati non producono alcuna riga di fallimento — vengono stampati solo add(2,3) verified == 5 e tag conditions verified, specchiando la regola di JUnit per cui un'asserzione soddisfatta è silenziosa e solo i fallimenti parlano.
  • assertThrows ha stampato caught expected IllegalArgumentException: name must not be null, mostrando il pattern: la lambda deve lanciare, il tipo deve corrispondere, e il messaggio dell'eccezione catturata è disponibile per ulteriori asserzioni.
  • assertAll ran 3 checks, 0 failed conferma il comportamento di raggruppamento — tutte e tre le lambda sono state eseguite e conteggiate insieme, ed è esattamente per questo che assertAll rivela ogni problema in un'unica passata invece di fermarsi al primo.
  • Il SUMMARY -> passed: 7, failed: 0 finale conta sette asserzioni individuali nell'intero programma (un'uguaglianza, due boolean, un throws e tre dentro assertAll), la stessa contabilità che riporta un runner reale — ogni chiamata assertX è una verifica.
  • Niente qui ha importato un framework di test, eppure la struttura è identica a Jupiter: helper silenziosi in caso di successo ed espliciti in caso di fallimento. Sostituire questi con org.junit.jupiter.api.Assertions.* cambierebbe gli import, non il modo in cui ragioni su ogni controllo.
Attenzione

L'ordine degli argomenti mette quasi tutti in difficoltà all'inizio: è assertEquals(expected, actual), mai il contrario. Invertirli e il test funziona lo stesso, ma un messaggio di fallimento risulta al contrario — afferma che il valore corretto è sbagliato e quello difettoso è giusto. Metti sempre il valore letterale o noto per primo.

Dove andare dopo

Le asserzioni sono solo un elemento di un metodo di test. Per usarle efficacemente:

Pratica

Pratica
In JUnit 5, cosa ti offre 'assertAll' che una sequenza di asserzioni separate non offre?
In JUnit 5, cosa ti offre 'assertAll' che una sequenza di asserzioni separate non offre?
Was this page helpful?