W3docs

Introduction à JUnit en Java

Découvrez JUnit, comment l'ajouter à un projet Java et comment écrire votre premier test JUnit.

JUnit est le framework de référence pour écrire des tests automatisés en Java. Un test est simplement une petite méthode qui exécute une partie de votre code et vérifie qu'il s'est comporté comme prévu ; le rôle de JUnit est de découvrir ces méthodes, d'exécuter chacune d'elles de manière isolée, de vérifier les assertions et de signaler lesquelles ont réussi et lesquelles ont échoué. La génération actuelle, JUnit 5 (également appelée JUnit Jupiter), est fournie sous la forme d'un ensemble de petites bibliothèques à ajouter à votre build — elle ne fait pas partie du JDK — et elle alimente l'étape mvn test / gradle test qui conditionne l'intégration continue de presque tous les projets Java.

Pourquoi un framework de test

Vous pourriez vérifier votre code manuellement avec des méthodes main et println — mais cela ne passe pas à l'échelle. Un framework vous offre quatre choses que vous devriez autrement reconstruire vous-même :

  • Découverte — il trouve automatiquement chaque méthode @Test ; vous n'avez jamais à maintenir une liste.
  • Isolation — chaque test dispose d'un contexte propre, de sorte qu'un test ne peut pas corrompre un autre.
  • Assertions — un vocabulaire riche (assertEquals, assertThrows, …) qui produit des messages d'échec précis.
  • Rapport — un résumé succès/échec uniforme que l'outil de build et l'IDE comprennent.

Faites cela une bonne fois et les tests deviennent peu coûteux à écrire, ce qui est tout l'intérêt : les tests peu coûteux sont effectivement écrits, et le code testé peut être modifié sans crainte.

Ajouter JUnit à un projet

JUnit 5 est une dépendance déclarée dans votre fichier de build. Avec Maven, l'agrégateur junit-jupiter inclut l'API (pour la compilation) et le moteur (pour l'exécution) :

<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter</artifactId>
  <version>5.11.3</version>
  <scope>test</scope>
</dependency>

Avec Gradle, ce sont deux lignes plus le commutateur useJUnitPlatform() :

dependencies {
  testImplementation 'org.junit.jupiter:junit-jupiter:5.11.3'
}
test {
  useJUnitPlatform()
}

Les sources de test résident sous src/test/java, en miroir du package de la classe qu'elles testent. Le scope test maintient JUnit hors de votre artifact de production.

Votre premier test

Un test JUnit est une méthode ordinaire annotée avec @Test. À l'intérieur, vous appelez le code à tester et vérifiez le résultat. Voici une Calculator et une classe de test pour celle-ci :

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

class CalculatorTest {

  private final Calculator calc = new Calculator();

  @Test
  void addReturnsSum() {
    assertEquals(5, calc.add(2, 3));
  }

  @Test
  void divideByZeroThrows() {
    assertThrows(ArithmeticException.class, () -> calc.divide(1, 0));
  }
}

Remarquez le schéma que suit chaque test — Arrange un contexte, Act en appelant la méthode, Assert le résultat. Les méthodes de test sont package-private (pas besoin de public dans JUnit 5) et retournent void. Exécutez mvn test et le build passe au vert uniquement si toutes les assertions sont vérifiées.

Les annotations et assertions les plus utilisées

La surface d'API de JUnit est réduite. Ces quelques membres couvrent la grande majorité des tests réels :

MembrePackage / classeRôle
@Testorg.junit.jupiter.apiMarque une méthode comme test
@BeforeEach / @AfterEachorg.junit.jupiter.apiS'exécute avant/après chaque test (mise en place / nettoyage des contextes)
@BeforeAll / @AfterAllorg.junit.jupiter.apiS'exécute une seule fois avant/après tous les tests de la classe
@DisplayNameorg.junit.jupiter.apiUn nom lisible par l'humain pour les rapports
@Disabledorg.junit.jupiter.apiIgnore temporairement un test
assertEquals(exp, act)AssertionsÉchoue si les deux valeurs ne sont pas égales
assertTrue / assertFalseAssertionsÉchoue si le boolean ne correspond pas
assertThrows(type, exec)AssertionsÉchoue si le lambda ne lève pas cette exception
assertNull / assertNotNullAssertionsÉchoue si la nullité est incorrecte

@BeforeEach est ce qui garantit à chaque test un état propre — JUnit construit une nouvelle instance de la classe de test pour chaque @Test, puis exécute la mise en place, de sorte que l'état ne fuite jamais entre les tests.

Ce que JUnit fait pour vous, dans un fichier exécutable

Le code runner ici n'a pas JUnit dans son classpath (c'est une bibliothèque externe, pas une partie du JDK), donc l'exemple ci-dessous réimplémente la boucle principale de JUnit en Java pur : un contexte recréé avant chaque test, un petit ensemble d'assistants assertXxx, une liste de méthodes de test exécutées indépendamment, et un bilan succès/échec à la fin. C'est exactement la mécanique que JUnit automatise — la voir à nu rend le vrai framework évident. Un test échoue intentionnellement pour que vous puissiez voir à quoi ressemble le rouge.

java— editable, runs on the server

Ce qu'il faut retenir de l'exécution :

  • Les trois tests corrects affichent PASS et le quatrième affiche FAIL deliberatelyFailing -> expected <10> but was <5> — un message d'échec précis, pas simplement "un test a échoué." Ce diff (expected … but was …) est exactement ce que le assertEquals de JUnit vous donne, et c'est ce qui rend un test rouge diagnostiquable en un coup d'œil.
  • setUp() s'exécute avant chaque test, donc calc est une nouvelle Calculator à chaque fois. C'est le contrat de @BeforeEach : les tests sont isolés, et l'ordre dans lequel ils s'exécutent n'a jamais d'importance car aucun d'eux ne partage un état mutable.
  • divideThrowsOnZero réussit en affirmant qu'une exception est levée — assertThrows fait de "cela devrait échouer" une assertion positive de première classe plutôt qu'un fragile try/catch. Les exceptions attendues sont un comportement qui mérite d'être testé, pas des erreurs à ignorer.
  • Le bilan final — Tests run: 4, Passed: 3, Failed: 1, Assertions: 5 — est le rapport. Un seul test qui échoue sur quatre fait passer l'ensemble du build en RED ; l'IC traite tout échec comme un arrêt, c'est pourquoi une suite verte est significative.
  • Rien ici n'a importé JUnit, pourtant la forme est identique : découverte des méthodes annotées, mise en place par test, assertions, résumé. La valeur de JUnit est d'automatiser cette boucle (et d'ajouter la découverte, le parallélisme, les tests paramétrés et l'intégration IDE) afin que vous n'écriviez que les corps des tests.

Ce que couvre le reste de cette partie

Cette partie s'appuie sur la boucle principale que vous venez de voir :

  • Les annotations JUnit@Test, @DisplayName, @Disabled, et le reste de l'ensemble des marqueurs.
  • Le cycle de vie du test — comment @BeforeEach / @AfterEach / @BeforeAll / @AfterAll donnent à chaque test un contexte propre.
  • Les assertions — le catalogue complet des assertXxx, y compris assertThrows pour tester les exceptions.
  • Les tests paramétrés — exécuter un corps de test sur de nombreuses entrées au lieu de copier-coller des cas.
  • Les mocks avec Mockito — remplacer les collaborateurs d'une classe par des substituts pour qu'un test unitaire reste un test unitaire.

Si vous souhaitez avoir une vue d'ensemble de l'importance des tests automatisés avant de plonger dans l'API, consultez Les tests en Java. Sinon, le prochain chapitre commence là où commence toute suite : définir une classe de test et l'exécuter.

Pratique

Pratique
Dans JUnit 5, que garantit l'annotation d'une méthode avec @BeforeEach concernant le contexte du test ?
Dans JUnit 5, que garantit l'annotation d'une méthode avec @BeforeEach concernant le contexte du test ?
Was this page helpful?