430 votes

JUnit 5 : Comment vérifier qu'une exception est levée ?

Existe-t-il un meilleur moyen d'affirmer qu'une méthode lance une exception dans JUnit 5?

Actuellement, je dois utiliser une @Rule pour vérifier que mon test lance une exception, mais cela ne fonctionne pas pour les cas où je m'attends à ce que plusieurs méthodes lancent des exceptions dans mon test.

2 votes

Vous pourriez être intéressé par la vérification AssertJ pour vérifier les exceptions car elle est plus flexible que JUnit5

0 votes

Voici un bel exemple sur comment vérifier qu'une exception est lancée dans JUnit4 et JUnit5.

0 votes

Si vous vous attendez à ce que plusieurs méthodes lancent des exceptions dans un même test, c'est une mauvaise pratique de code; vous voulez probablement écrire plusieurs tests, un pour chaque exception lancée

751voto

steventrouble Points 71

Vous pouvez utiliser assertThrows(), qui vous permet de tester plusieurs exceptions dans le même test. Avec le support des lambda en Java 8, c'est la manière canonique de tester les exceptions dans JUnit.

Selon la documentation de JUnit:

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

@Test
void exceptionTesting() {
    MyException thrown = assertThrows(
           MyException.class,
           () -> myObject.doThing(),
           "Expected doThing() to throw, but it didn't"
    );

    assertTrue(thrown.getMessage().contentEquals("Stuff"));
}

19 votes

À partir d'un "old-school "Je ne sais pas grand-chose sur Junit5 et probablement pas assez sur Java8"... cela semble plutôt bizarre. Pourriez-vous ajouter quelques explications supplémentaires; comme "quelle partie est le véritable 'code de production' testé... qui devrait être supposé lancer"?

3 votes

() -> pointe vers une expression lambda qui accepte zéro arguments. Ainsi, le "code de production" censé déclencher l'exception se trouve dans le bloc de code pointé (c'est-à-dire, l'instruction throw new... à l'intérieur des accolades).

2 votes

Généralement, l'expression lambda interagirait avec le sujet de test (SUT). En d'autres termes, le fait de jeter directement une exception comme ci-dessus est simplement à des fins de démonstration.

142voto

prime Points 1239

En Java 8 et JUnit 5 (Jupiter), nous pouvons faire des assertions pour les exceptions comme suit. Utilisation de org.junit.jupiter.api.Assertions.assertThrows

public static < T extends Throwable > T assertThrows(Class< T > expectedType, Executable executable)

Asserte que l'exécution de l'exécutable fourni lance une exception du expectedType et retourne l'exception.

Si aucune exception n'est lancée, ou si une exception d'un type différent est levée, cette méthode échouera.

Si vous ne souhaitez pas effectuer de vérifications supplémentaires sur l'instance de l'exception, ignorez simplement la valeur de retour.

@Test
public void itShouldThrowNullPointerExceptionWhenBlahBlah() {
    assertThrows(NullPointerException.class,
            ()->{
            //faites ce que vous voulez faire ici
            //ex : objectName.thisMethodShoulThrowNullPointerExceptionForNullParameter(null);
            });
}

Cette approche utilisera l'Interface Fonctionnelle Executable dans org.junit.jupiter.api.

Référence :

1 votes

En haut avec celui-ci! C'est la meilleure réponse de loin, la plus à jour avec JUnit 5. De plus, IntelliJ condense encore davantage la lambda s'il n'y a qu'une seule ligne à la Lambda : assertThrows(NoSuchElementException.class, myLinkedList::getFirst);

0 votes

@anon58192932 cela ne se produit pas parce qu'il n'y a qu'une seule ligne, mais parce que vous n'appelez qu'une seule méthode (bien que cela ne serait, naturellement, également qu'une seule ligne automatiquement, à moins que vous n'ayez un nom de méthode interminable). Autrement dit : objet -> objet.méthodeAppel() peut être remplacé par Objet::méthodeAppel, mais vous ne pouvez pas remplacer objet -> objet.getUnTruc().getUnAutreTruc().faireQuelqueChose() par un appel de méthode.

0 votes

Merci pour Assertions.assertThrows().

40voto

jstar Points 352

Ils l'ont changé dans JUnit 5 (attendu : InvalidArgumentException, réel : méthode invoquée) et le code ressemble à ceci :

@Test
public void mauvaiseEntrée() {
    Throwable exception = assertThrows(InvalidArgumentException.class,
            ()->{objectName.yourMethod("WRONG");} );
}

29voto

Maintenant, Junit5 fournit une façon de vérifier les exceptions

Vous pouvez tester à la fois les exceptions générales et les exceptions personnalisées

Un scénario d'exception générale:

ExpectGeneralException.java

public void validateParameters(Integer param ) {
    if (param == null) {
        throw new NullPointerException("Les paramètres nuls ne sont pas autorisés");
    }
}

ExpectGeneralExceptionTest.java

@Test
@DisplayName("Test d'assertion NullPointerException")
void testGeneralException(TestInfo testInfo) {
    final ExpectGeneralException generalEx = new ExpectGeneralException();

     NullPointerException exception = assertThrows(NullPointerException.class, () -> {
            generalEx.validateParameters(null);
        });
    assertEquals("Les paramètres nuls ne sont pas autorisés", exception.getMessage());
}

Vous pouvez trouver un exemple pour tester CustomException ici : exemple de code d'exception

ExpectCustomException.java

public String constructErrorMessage(String... args) throws InvalidParameterCountException {
    if(args.length!=3) {
        throw new InvalidParameterCountException("Compteur de paramètres invalide : attendu=3, passé="+args.length);
    }else {
        String message = "";
        for(String arg: args) {
            message += arg;
        }
        return message;
    }
}

ExpectCustomExceptionTest.java

@Test
@DisplayName("Test d'assertion d'exception")
void testCustomException(TestInfo testInfo) {
    final ExpectCustomException expectEx = new ExpectCustomException();

     InvalidParameterCountException exception = assertThrows(InvalidParameterCountException.class, () -> {
            expectEx.constructErrorMessage("exemple ","erreur");
        });
    assertEquals("Compteur de paramètres invalide : attendu=3, passé=2", exception.getMessage());
}

1 votes

Il n'y a pas de différence dans la façon dont JUnit gère les exceptions intégrées et personnalisées.

11voto

JesseBoyd Points 377

Je pense que c'est un exemple encore plus simple

List emptyList = new ArrayList<>();
Optional opt2 = emptyList.stream().findFirst();
assertThrows(NoSuchElementException.class, () -> opt2.get());

Appeler get() sur un facultatif contenant une ArrayList vide lancera une NoSuchElementException. assertThrows déclare l'exception attendue et fournit un fournisseur lambda (ne prend pas d'arguments et renvoie une valeur).

Merci à @prime pour sa réponse sur laquelle j'ai espérons-le, élaboré.

1 votes

La méthode assertThrows retourne l'exception déclenchée. Donc vous pouvez faire comme NoSuchElementException e = assertThrows(NoSuchElementException.class, () -> opt2.get()); puis en dessous vous pouvez faire toutes sortes d'assertions sur l'objet exception que vous voulez.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X