web-dev-qa-db-fra.com

Comment affirmez-vous qu'une certaine exception est générée dans les tests JUnit 4?

Comment utiliser JUnit4 de manière idiomatique pour vérifier que du code lève une exception?

Bien que je puisse certainement faire quelque chose comme ça:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  boolean thrown = false;

  try {
    foo.doStuff();
  } catch (IndexOutOfBoundsException e) {
    thrown = true;
  }

  assertTrue(thrown);
}

Je rappelle qu’il existe une annotation ou un Assert.xyz ou quelque chose qui est beaucoup moins discret et beaucoup plus dans l’esprit de JUnit pour ce genre de situation.

1883
SCdF

JUnit 4 supporte ceci:

@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);
}

Référence: https://junit.org/junit4/faq.html#atests_7

2253
skaffman

Edit Maintenant que JUnit5 est sorti, la meilleure option serait d’utiliser Assertions.assertThrows() (voir mon autre réponse ).

Si vous n'avez pas migré vers JUnit 5, mais que vous pouvez utiliser JUnit 4.7, vous pouvez utiliser la règle ExpectedException :

public class FooTest {
  @Rule
  public final ExpectedException exception = ExpectedException.none();

  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    exception.expect(IndexOutOfBoundsException.class);
    foo.doStuff();
  }
}

C'est beaucoup mieux que @Test(expected=IndexOutOfBoundsException.class) parce que le test échouera si IndexOutOfBoundsException est lancé avant foo.doStuff()

Voir cet article pour plus de détails

1266
NamshubWriter

Faites attention en utilisant l'exception attendue, car elle affirme seulement que la méthode a lancé cette exception, pas une ligne de code particulière dans le test.

J'ai tendance à utiliser ceci pour tester la validation des paramètres, car de telles méthodes sont généralement très simples, mais des tests plus complexes pourraient être mieux servis avec:

try {
    methodThatShouldThrow();
    fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}

Appliquer le jugement.

456
daveb

Comme indiqué précédemment, il existe de nombreuses façons de traiter les exceptions dans JUnit. Mais avec Java 8, il en existe un autre: utiliser les expressions lambda. Avec les expressions lambda, nous pouvons obtenir une syntaxe comme celle-ci:

@Test
public void verifiesTypeAndMessage() {
    assertThrown(new DummyService()::someMethod)
            .isInstanceOf(RuntimeException.class)
            .hasMessage("Runtime exception occurred")
            .hasMessageStartingWith("Runtime")
            .hasMessageEndingWith("occurred")
            .hasMessageContaining("exception")
            .hasNoCause();
}

assertThrown accepte une interface fonctionnelle, dont les instances peuvent être créées avec des expressions lambda, des références de méthode ou des références de constructeur. assertThrown acceptant cette interface attendra et sera prêt à gérer une exception.

C'est une technique relativement simple mais puissante.

Jetez un coup d'œil à cet article de blog décrivant cette technique: http://blog.codeleak.pl/2014/07/junit-testing-exception-with-Java-8-and-lambda-expressions.html =

Le code source peut être trouvé ici: https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/Java/com/github/kolorobot/exceptions/Java8

Divulgation: Je suis l'auteur du blog et du projet.

206
Rafal Borowiec

dans junit, il y a quatre façons de tester l'exception.

junit5.x

  • pour junit5.x, vous pouvez utiliser assertThrows comme suit

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff());
        assertEquals("expected messages", exception.getMessage());
    }
    

junit4.x

  • pour junit4.x, utilisez l'attribut facultatif 'attendu' de Test d'annonce

    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
    
  • pour junit4.x, utilisez la règle ExpectedException

    public class XxxTest {
        @Rule
        public ExpectedException thrown = ExpectedException.none();
    
        @Test
        public void testFooThrowsIndexOutOfBoundsException() {
            thrown.expect(IndexOutOfBoundsException.class)
            //you can test the exception message like
            thrown.expectMessage("expected messages");
            foo.doStuff();
        }
    }
    
  • vous pouvez également utiliser la méthode classique try/catch largement utilisée dans le cadre de junit 3

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        try {
            foo.doStuff();
            fail("expected exception was not occured.");
        } catch(IndexOutOfBoundsException e) {
            //if execution reaches here, 
            //it indicates this exception was occured.
            //so we need not handle it.
        }
    }
    
  • alors

    • si vous aimez junit 5, alors vous devriez aimer le premier
    • la 2ème manière est utilisée lorsque vous souhaitez uniquement tester le type d'exception
    • les premier et dernier deux sont utilisés lorsque vous voulez tester le message d'exception plus loin
    • si vous utilisez Junit 3, le 4ème est préféré
  • pour plus d'informations, vous pouvez lire ce document et guide de l'utilisateur junit5 pour plus de détails.

127
walsh

tl; dr

  • pré-JDK8: Je recommanderai le bon vieux bloc try-catch. (N'oubliez pas d'ajouter une assertion fail() avant le bloc catch)

  • post-JDK8: utilisez AssertJ ou des lambdas personnalisés pour affirmer le comportement exceptionnel.

_ {Indépendamment de Junit 4 ou JUnit 5.} _

la longue histoire

Vous pouvez écrire vous-même un bloc faites-le vous-même} _ try-catch ou utiliser les outils JUnit (@Test(expected = ...) ou la fonction de règle _@Rule ExpectedException_ JUnit).

Mais ces manières ne sont pas si élégantes et ne se mélangent pas bien lisibilité avec d'autres outils. De plus, l'outillage JUnit présente des pièges.

  1. Le bloc try-catch vous devez écrire le bloc autour du comportement testé et écrire l'assertion dans le bloc catch, c'est correct, mais beaucoup trouvent que ce style interrompt le flux de lecture d'un test. Vous devez également écrire un _Assert.fail_ à la fin du bloc try, sinon le test risque de manquer un côté des assertions; PMD _ _ findbugs _ ou Sonar_ repérera de tels problèmes.

  2. La fonction @Test(expected = ...) est intéressante car vous pouvez écrire moins de code et écrire ce test est supposé moins sujet aux erreurs de codage. MaisCette approche manque de quelques zones.

    • Si le test doit vérifier des éléments supplémentaires sur l'exception, tels que la cause ou le message (de bons messages d'exception sont vraiment importants, disposer d'un type d'exception précis peut ne pas suffire).
    • De plus, comme l'attente est placée dans la méthode, en fonction de la manière dont le code testé est écrit, une mauvaise partie du code de test peut renvoyer l'exception, ce qui conduit à un test faussement positif et je ne suis pas sûr que PMD, findbugs _ ou Sonar _ donnera des indications sur ce code.

      _@Test(expected = WantedException.class)
      public void call2_should_throw_a_WantedException__not_call1() {
          // init tested
          tested.call1(); // may throw a WantedException
      
          // call to be actually tested
          tested.call2(); // the call that is supposed to raise an exception
      }
      _
  3. La règle ExpectedException est également une tentative de correction des mises en garde précédentes, mais elle est un peu délicate à utiliser car elle utilise un style d’attente, EasyMock, les utilisateurs connaissent très bien ce style. Cela peut être pratique pour certains, mais si vous suivez les principes Développement dirigé par le comportement (BDD) ou Arrange Act Assert (AAA), la règle ExpectedException ne cadrera pas avec le style d'écriture utilisé. En plus de cela, il peut souffrir du même problème que celui de la manière _@Test_, selon l'endroit où vous vous attendez.

    _@Rule ExpectedException thrown = ExpectedException.none()
    
    @Test
    public void call2_should_throw_a_WantedException__not_call1() {
        // expectations
        thrown.expect(WantedException.class);
        thrown.expectMessage("boom");
    
        // init tested
        tested.call1(); // may throw a WantedException
    
        // call to be actually tested
        tested.call2(); // the call that is supposed to raise an exception
    }
    _

    Même si l'exception attendue est placée avant l'instruction de test, cela interrompt votre lecture si les tests suivent BDD ou AAA.

    Voir aussi ce comment numéro sur JUnit de l'auteur de ExpectedException. JUnit 4.13-beta-2 == décourage même ce mécanisme:

    Demande d'extraction # 1519 : Obsolète ExpectedException

    La méthode Assert.assertThrows offre un moyen plus pratique de vérifier les exceptions. De plus, l'utilisation de ExpectedException est sujette aux erreurs lorsqu'elle est utilisée avec d'autres règles telles que TestWatcher, car l'ordre des règles est important dans ce cas.

Ainsi, ces options ont toutes leurs mises en garde et ne sont clairement pas à l'abri des erreurs de programmation.

  1. Il y a un projet que j'ai découvert après avoir créé cette réponse qui semble prometteur: catch-exception .

    Comme le dit la description du projet, il permet à un codeur d'écrire dans une ligne de code courante capturant l'exception et de proposer cette exception pour une assertion ultérieure. Et vous pouvez utiliser n'importe quelle bibliothèque d'assertions comme Hamcrest ou AssertJ .

    Un exemple rapide tiré de la page d'accueil:

    _// given: an empty list
    List myList = new ArrayList();
    
    // when: we try to get the first element of the list
    when(myList).get(1);
    
    // then: we expect an IndexOutOfBoundsException
    then(caughtException())
            .isInstanceOf(IndexOutOfBoundsException.class)
            .hasMessage("Index: 1, Size: 0") 
            .hasNoCause();
    _

    Comme vous pouvez voir que le code est vraiment simple, vous attrapez l'exception sur une ligne spécifique, l'API then est un alias qui utilisera les API AssertJ (similaire à l'utilisation de assertThat(ex).hasNoCause()...). _ {À un moment donné, le projet s'est appuyé sur FEST-Assert, l'ancêtre de AssertJ}. EDIT:Il semble que le projet prépare un support Java 8 Lambdas.

    Actuellement, cette bibliothèque a deux inconvénients:

    • Au moment d'écrire ces lignes, il est intéressant de noter que cette bibliothèque est basée sur Mockito 1.x car elle crée une maquette de l'objet testé derrière la scène. Comme Mockito n'est toujours pas mis à jour , cette bibliothèque ne peut pas fonctionner avec les classes finales ou les méthodes finales. Et même s'il était basé sur mockito 2 dans la version actuelle, cela nécessiterait de déclarer un fabricant de maquette global (_inline-mock-maker_), ce qui peut ne pas être ce que vous voulez, car ce simulateur présente des inconvénients différents de ceux du simulateur habituel.

    • Cela nécessite encore une autre dépendance de test.

    Ces problèmes ne s'appliqueront pas une fois que la bibliothèque aura pris en charge lambdas. Toutefois, les fonctionnalités seront dupliquées par le jeu d'outils AssertJ.

    Si vous ne souhaitez pas utiliser l'outil catch-exception, je vous recommanderai l'ancien bon moyen du bloc try-catch, du moins jusqu'au JDK7. Et pour les utilisateurs de JDK 8, vous préférerez peut-être utiliser AssertJ car il offre plus que la simple affirmation d’exceptions.

  2. Avec la JDK8, les lambdas entrent dans la scène des tests et se sont révélés être un moyen intéressant d’affirmer un comportement exceptionnel. AssertJ a été mis à jour pour fournir une API couramment utilisée par Nice afin d'affirmer un comportement exceptionnel.

    Et un exemple de test avec AssertJ :

    _@Test
    public void test_exception_approach_1() {
        ...
        assertThatExceptionOfType(IOException.class)
                .isThrownBy(() -> someBadIOOperation())
                .withMessage("boom!"); 
    }
    
    @Test
    public void test_exception_approach_2() {
        ...
        assertThatThrownBy(() -> someBadIOOperation())
                .isInstanceOf(Exception.class)
                .hasMessageContaining("boom");
    }
    
    @Test
    public void test_exception_approach_3() {
        ...
        // when
        Throwable thrown = catchThrowable(() -> someBadIOOperation());
    
        // then
        assertThat(thrown).isInstanceOf(Exception.class)
                          .hasMessageContaining("boom");
    }
    _
  3. Avec une réécriture presque complète de JUnit 5, les assertions ont été amélioré un peu, elles peuvent s'avérer intéressantes en tant que solution prête à l'emploi pour affirmer correctement une exception. Mais en réalité, l’API d’affirmation est encore un peu pauvre, il n’ya rien en dehors de assertThrows .

    _@Test
    @DisplayName("throws EmptyStackException when peeked")
    void throwsExceptionWhenPeeked() {
        Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek());
    
        Assertions.assertEquals("...", t.getMessage());
    }
    _

    Comme vous l'avez remarqué, assertEquals renvoie toujours void et, en tant que tel, ne permet pas l'enchaînement d'assertions telles qu'AssertJ.

    De même, si vous vous souvenez du conflit entre les noms et Matcher ou Assert, soyez prêt à affronter le même conflit avec Assertions.

Je voudrais conclure qu'aujourd'hui (2017-03-03) la facilité d'utilisation de AssertJ, son API détectable, son rythme de développement rapide et en tant que de facto La dépendance de test est la meilleure solution avec JDK8, quel que soit le cadre de test (JUnit ou non), les JDK antérieurs doivent plutôt s'appuyer sur try-catchblocs même s'ils se sentent maladroits.

_ {Cette réponse a été copiée de une autre question == qui n'a pas la même visibilité, je suis le même auteur.} _

102
Brice

Maintenant que JUnit 5 est disponible, la meilleure option consiste à utiliser Assertions.assertThrows() (voir le Guide de l'utilisateur Junit 5 ).

Voici un exemple qui vérifie qu'une exception a été levée et utilise Truth pour faire des assertions sur le message de l'exception:

public class FooTest {
  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    IndexOutOfBoundsException e = assertThrows(
        IndexOutOfBoundsException.class, foo::doStuff);

    assertThat(e).hasMessageThat().contains("woops!");
  }
}

Les avantages par rapport aux approches des autres réponses sont les suivants:

  1. Construit dans JUnit
  2. Vous obtenez un message d’exception utile si le code de la lambda ne lève pas d’exception et un stacktrace s’il lève une exception différente.
  3. Concis
  4. Permet à vos tests de suivre Arrange-Act-Assert
  5. Vous pouvez indiquer avec précision le code que vous attendez de lancer l'exception
  6. Vous n'avez pas besoin de répertorier l'exception attendue dans la clause throws
  7. Vous pouvez utiliser le cadre d’affirmation de votre choix pour formuler des affirmations sur l’exception interceptée.

Une méthode similaire sera ajoutée à org.junit Assert dans JUnit 4.13.

55
NamshubWriter

Que diriez-vous de ceci: Catch une exception très générale, assurez-vous qu’il le sort du bloc catch, puis affirmez que la classe de l’exception correspond à ce que vous attendez. Cette assertion échouera si a) l'exception est d'un type incorrect (par exemple, si vous avez plutôt un pointeur Null) et b) si l'exception n'a pas été levée.

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

  try {
    foo.doStuff();
  } catch (Throwable ex) {
    e = ex;
  }

  assertTrue(e instanceof IndexOutOfBoundsException);
}
40
Johan

BDD Solution de style: JUnit 4 + exception de capture + AssertJ

@Test
public void testFooThrowsIndexOutOfBoundsException() {

    when(foo).doStuff();

    then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);

}

Code source

Les dépendances

eu.codearte.catch-exception:catch-exception:1.3.3
34
MariuszS

Utiliser une assertion AssertJ , qui peut être utilisée avec JUnit:

import static org.assertj.core.api.Assertions.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  Foo foo = new Foo();

  assertThatThrownBy(() -> foo.doStuff())
        .isInstanceOf(IndexOutOfBoundsException.class);
}

C'est mieux que @Test(expected=IndexOutOfBoundsException.class) parce que cela garantit la ligne attendue dans le test et permet de vérifier plus de détails sur l'exception, comme le message, plus facilement:

assertThatThrownBy(() ->
       {
         throw new Exception("boom!");
       })
    .isInstanceOf(Exception.class)
    .hasMessageContaining("boom");

Instructions Maven/Gradle ici.

33
weston

Pour résoudre le même problème, j’ai mis en place un petit projet: http://code.google.com/p/catch-exception/

En utilisant ce petit assistant, vous écririez

verifyException(foo, IndexOutOfBoundsException.class).doStuff();

Ceci est moins détaillé que la règle ExpectedException de JUnit 4.7. Par rapport à la solution fournie par skaffman, vous pouvez spécifier dans quelle ligne de code vous souhaitez l'exception. J'espère que ça aide.

32
rwitzel

Mise à jour: JUnit5 présente une amélioration pour le test des exceptions: assertThrows.

l'exemple suivant est tiré de: Guide de l'utilisateur Junit 5

 @Test
void exceptionTesting() {
    Throwable exception = assertThrows(IllegalArgumentException.class, () -> 
    {
        throw new IllegalArgumentException("a message");
    });
    assertEquals("a message", exception.getMessage());
}

Réponse originale en utilisant JUnit 4.

Il y a plusieurs façons de tester qu'une exception est levée. J'ai également discuté des options ci-dessous dans mon article Comment écrire de bons tests unitaires avec JUnit

Définissez le paramètre expected@Test(expected = FileNotFoundException.class).

@Test(expected = FileNotFoundException.class) 
public void testReadFile() { 
    myClass.readFile("test.txt");
}

Utilisation de trycatch

public void testReadFile() { 
    try {
        myClass.readFile("test.txt");
        fail("Expected a FileNotFoundException to be thrown");
    } catch (FileNotFoundException e) {
        assertThat(e.getMessage(), is("The file test.txt does not exist!"));
    }

}

Test avec la règle ExpectedException.

@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void testReadFile() throws FileNotFoundException {

    thrown.expect(FileNotFoundException.class);
    thrown.expectMessage(startsWith("The file test.txt"));
    myClass.readFile("test.txt");
}

Pour en savoir plus sur le test des exceptions dans JUnit4 wiki for Exception testing et bad.robot - Exceptioning Exceptions JUnit Rule .

30
Dilini Rajapaksha

Vous pouvez aussi faire ceci:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
    try {
        foo.doStuff();
        assert false;
    } catch (IndexOutOfBoundsException e) {
        assert true;
    }
}
21
John Mikic

IMHO, le meilleur moyen de vérifier les exceptions dans JUnit est le modèle try/catch/fail/assert:

// this try block should be as small as possible,
// as you want to make sure you only catch exceptions from your code
try {
    sut.doThing();
    fail(); // fail if this does not throw any exception
} catch(MyException e) { // only catch the exception you expect,
                         // otherwise you may catch an exception for a dependency unexpectedly
    // a strong assertion on the message, 
    // in case the exception comes from anywhere an unexpected line of code,
    // especially important if your checking IllegalArgumentExceptions
    assertEquals("the message I get", e.getMessage()); 
}

Le assertTrue pourrait être un peu fort pour certaines personnes, donc assertThat(e.getMessage(), containsString("the message"); pourrait être préférable.

13
Alex Collins

Solution JUnit 5

@Test
void testFooThrowsIndexOutOfBoundsException() {    
  Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );

  assertEquals( "some message", exception.getMessage() );
}

Plus d'infos sur JUnit 5 sur http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions

12
Daniel Käfer

J'ai essayé beaucoup de méthodes ici, mais elles étaient soit compliquées, soit ne répondaient pas tout à fait à mes exigences. En fait, on peut écrire une méthode d'assistance très simplement:

public class ExceptionAssertions {
    public static void assertException(BlastContainer blastContainer ) {
        boolean caughtException = false;
        try {
            blastContainer.test();
        } catch( Exception e ) {
            caughtException = true;
        }
        if( !caughtException ) {
            throw new AssertionFailedError("exception expected to be thrown, but was not");
        }
    }
    public static interface BlastContainer {
        public void test() throws Exception;
    }
}

Utilisez-le comme ceci:

assertException(new BlastContainer() {
    @Override
    public void test() throws Exception {
        doSomethingThatShouldExceptHere();
    }
});

Dépendances zéro: pas besoin de mockito, pas besoin de powermock; et fonctionne très bien avec les classes finales.

11
Hugh Perkins

La réponse la plus souple et la plus élégante pour Junit 4 trouvée dans le blog de Mkyong . Il a la flexibilité du try/catch en utilisant l'annotation @Rule. J'aime cette approche car vous pouvez lire les attributs spécifiques d'une exception personnalisée.

package com.mkyong;

import com.mkyong.examples.CustomerService;
import com.mkyong.examples.exception.NameNotFoundException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.hasProperty;

public class Exception3Test {

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void testNameNotFoundException() throws NameNotFoundException {

        //test specific type of exception
        thrown.expect(NameNotFoundException.class);

        //test message
        thrown.expectMessage(is("Name is empty!"));

        //test detail
        thrown.expect(hasProperty("errCode"));  //make sure getters n setters are defined.
        thrown.expect(hasProperty("errCode", is(666)));

        CustomerService cust = new CustomerService();
        cust.findByName("");

    }

}
10
Dherik

Solution Java 8

Si vous souhaitez une solution qui:

  • Utilise Java 8 lambdas
  • ne dépend pas de la magie de JUnit
  • Vous permet de vérifier plusieurs exceptions dans une seule méthode de test
  • Vérifie qu'une exception est générée par un ensemble spécifique de lignes dans votre méthode de test au lieu de toute ligne inconnue dans la méthode de test complète
  • Renvoie l'objet exception réel qui a été lancé afin que vous puissiez l'examiner plus en détail.

Voici une fonction utilitaire que j'ai écrite:

public final <T extends Throwable> T expectException( Class<T> exceptionClass, Runnable runnable )
{
    try
    {
        runnable.run();
    }
    catch( Throwable throwable )
    {
        if( throwable instanceof AssertionError && throwable.getCause() != null )
            throwable = throwable.getCause(); //allows "assert x != null : new IllegalArgumentException();"
        assert exceptionClass.isInstance( throwable ) : throwable; //exception of the wrong kind was thrown.
        assert throwable.getClass() == exceptionClass : throwable; //exception thrown was a subclass, but not the exact class, expected.
        @SuppressWarnings( "unchecked" )
        T result = (T)throwable;
        return result;
    }
    assert false; //expected exception was not thrown.
    return null; //to keep the compiler happy.
}

( extrait de mon blog )

Utilisez-le comme suit:

@Test
public void testThrows()
{
    RuntimeException e = expectException( RuntimeException.class, () -> 
        {
            throw new RuntimeException( "fail!" );
        } );
    assert e.getMessage().equals( "fail!" );
}
9
Mike Nakis

JUnit a un support intégré pour cela, avec un attribut "attendu"

8
Mark Bessey

Dans mon cas, je reçois toujours une exception RuntimeException de la base de données, mais les messages diffèrent. Et les exceptions doivent être gérées respectivement. Voici comment je l'ai testé:

@Test
public void testThrowsExceptionWhenWrongSku() {

    // Given
    String articleSimpleSku = "999-999";
    int amountOfTransactions = 1;
    Exception exception = null;

    // When
    try {
        createNInboundTransactionsForSku(amountOfTransactions, articleSimpleSku);
    } catch (RuntimeException e) {
        exception = e;
    }

    // Then
    shouldValidateThrowsExceptionWithMessage(exception, MESSAGE_NON_EXISTENT_SKU);
}

private void shouldValidateThrowsExceptionWithMessage(final Exception e, final String message) {
    assertNotNull(e);
    assertTrue(e.getMessage().contains(message));
}
7
Macchiatow

Dans JUnit 4 ou version ultérieure, vous pouvez tester les exceptions comme suit

@Rule
public ExpectedException exceptions = ExpectedException.none();


Ceci fournit de nombreuses fonctionnalités qui peuvent être utilisées pour améliorer nos tests JUnit.
Si vous voyez l'exemple ci-dessous, je teste 3 choses sur l'exception.

  1. Le type d'exception levée
  2. Le message d'exception
  3. La cause de l'exception


public class MyTest {

    @Rule
    public ExpectedException exceptions = ExpectedException.none();

    ClassUnderTest classUnderTest;

    @Before
    public void setUp() throws Exception {
        classUnderTest = new ClassUnderTest();
    }

    @Test
    public void testAppleisSweetAndRed() throws Exception {

        exceptions.expect(Exception.class);
        exceptions.expectMessage("this is the exception message");
        exceptions.expectCause(Matchers.<Throwable>equalTo(exceptionCause));

        classUnderTest.methodUnderTest("param1", "param2");
    }

}
5
Jobin Joseph

Faites juste un Matcher qui peut être activé et désactivé, comme ceci:

public class ExceptionMatcher extends BaseMatcher<Throwable> {
    private boolean active = true;
    private Class<? extends Throwable> throwable;

    public ExceptionMatcher(Class<? extends Throwable> throwable) {
        this.throwable = throwable;
    }

    public void on() {
        this.active = true;
    }

    public void off() {
        this.active = false;
    }

    @Override
    public boolean matches(Object object) {
        return active && throwable.isAssignableFrom(object.getClass());
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("not the covered exception type");
    }
}

Pour l'utiliser:

ajoutez public ExpectedException exception = ExpectedException.none();, puis:

ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
exception.expect(exMatch);
someObject.somethingThatThrowsMyException();
exMatch.off();
5
Tor P

Nous pouvons utiliser une assertion échouée après la méthode qui doit renvoyer une exception:

try{
   methodThatThrowMyException();
   Assert.fail("MyException is not thrown !");
} catch (final Exception exception) {
   // Verify if the thrown exception is instance of MyException, otherwise throws an assert failure
   assertTrue(exception instanceof MyException, "An exception other than MyException is thrown !");
   // In case of verifying the error message
   MyException myException = (MyException) exception;
   assertEquals("EXPECTED ERROR MESSAGE", myException.getMessage());
}
5
Shessuky

En plus de ce que NamShubWriter a dit, assurez-vous que:

  • L'instance ExpectedException est publique ( Question connexe )
  • ExpectedException n'est pas instancié, par exemple, dans la méthode @Avant. Ce post explique clairement toutes les subtilités de l'ordre d'exécution de JUnit.

Ne pas faites ceci:

@Rule    
public ExpectedException expectedException;

@Before
public void setup()
{
    expectedException = ExpectedException.none();
}

Enfin, this le blog illustre clairement comment affirmer qu'une certaine exception est levée.

4
Bruce Wayne

Je recommande la bibliothèque assertj-core pour gérer les exceptions dans les tests junit

Dans Java 8, comme ceci:

//given

//when
Throwable throwable = catchThrowable(() -> anyService.anyMethod(object));

//then
AnyException anyException = (AnyException) throwable;
assertThat(anyException.getMessage()).isEqualTo("........");
assertThat(exception.getCode()).isEqualTo(".......);
3
Piotr R

La solution Junit4 avec Java8 consiste à utiliser cette fonction:

public Throwable assertThrows(Class<? extends Throwable> expectedException, Java.util.concurrent.Callable<?> funky) {
    try {
        funky.call();
    } catch (Throwable e) {
        if (expectedException.isInstance(e)) {
            return e;
        }
        throw new AssertionError(
                String.format("Expected [%s] to be thrown, but was [%s]", expectedException, e));
    }
    throw new AssertionError(
            String.format("Expected [%s] to be thrown, but nothing was thrown.", expectedException));
}

L'utilisation est alors:

    assertThrows(ValidationException.class,
            () -> finalObject.checkSomething(null));

Notez que la seule limitation consiste à utiliser une référence d'objet final dans l'expression lambda. Cette solution permet de continuer les assertions de test au lieu de s’attendre à ce que la méthode soit utilisable au niveau de la méthode avec la solution @Test(expected = IndexOutOfBoundsException.class).

2
Donatello

Prenez par exemple, vous voulez écrire Junit pour le fragment de code mentionné ci-dessous

public int divideByZeroDemo(int a,int b){

    return a/b;
}

public void exceptionWithMessage(String [] arr){

    throw new ArrayIndexOutOfBoundsException("Array is out of bound");
}

Le code ci-dessus est destiné à tester l'existence d'une exception inconnue et le code ci-dessous indique une exception avec un message personnalisé.

 @Rule
public ExpectedException exception=ExpectedException.none();

private Demo demo;
@Before
public void setup(){

    demo=new Demo();
}
@Test(expected=ArithmeticException.class)
public void testIfItThrowsAnyException() {

    demo.divideByZeroDemo(5, 0);

}

@Test
public void testExceptionWithMessage(){


    exception.expectMessage("Array is out of bound");
    exception.expect(ArrayIndexOutOfBoundsException.class);
    demo.exceptionWithMessage(new String[]{"This","is","a","demo"});
}
1
Shirsh Sinha
    @Test(expectedException=IndexOutOfBoundsException.class) 
    public void  testFooThrowsIndexOutOfBoundsException() throws Exception {
         doThrow(IndexOutOfBoundsException.class).when(foo).doStuff();  
         try {
             foo.doStuff(); 
            } catch (IndexOutOfBoundsException e) {
                       assertEquals(IndexOutOfBoundsException .class, ex.getCause().getClass());
                      throw e;

               }

    }

Voici un autre moyen de vérifier la méthode levée ou non.

0
MangduYogii

Il y a deux façons d'écrire un cas de test

  1. Annotez le test avec l'exception qui est levée par la méthode. Quelque chose comme ça @Test(expected = IndexOutOfBoundsException.class)
  2. Vous pouvez simplement intercepter l'exception dans la classe de test à l'aide du bloc try catch et affirmer le message envoyé par la méthode de la classe de test.

    try{
    }
    catch(exception to be thrown from method e)
    {
         assertEquals("message", e.getmessage());
    }
    

J'espère que cela répond à votre requête. Bon apprentissage ...

0
Mohit ladia

Avec Java 8, vous pouvez créer une méthode prenant comme code un code à vérifier et une exception attendue:

private void expectException(Runnable r, Class<?> clazz) { 
    try {
      r.run();
      fail("Expected: " + clazz.getSimpleName() + " but not thrown");
    } catch (Exception e) {
      if (!clazz.isInstance(e)) fail("Expected: " + clazz.getSimpleName() + " but " + e.getClass().getSimpleName() + " found", e);
    }
  }

et ensuite dans votre test:

expectException(() -> list.sublist(0, 2).get(2), IndexOutOfBoundsException.class);

Avantages:

  • ne pas compter sur aucune bibliothèque
  • contrôle localisé - plus précis et permet d'avoir plusieurs affirmations de ce type dans un test si nécessaire
  • facile à utiliser
0
fahrenx

Ma solution utilisant Java 8 lambdas:

public static <T extends Throwable> T assertThrows(Class<T> expected, ThrowingRunnable action) throws Throwable {
    try {
        action.run();
        Assert.fail("Did not throw expected " + expected.getSimpleName());
        return null; // never actually
    } catch (Throwable actual) {
        if (!expected.isAssignableFrom(actual.getClass())) { // runtime '!(actual instanceof expected)'
            System.err.println("Threw " + actual.getClass().getSimpleName() 
                               + ", which is not a subtype of expected " 
                               + expected.getSimpleName());
            throw actual; // throw the unexpected Throwable for maximum transparency
        } else {
            return (T) actual; // return the expected Throwable for further examination
        }
    }
}

Vous devez définir une FunctionalInterface, car Runnable ne déclare pas le nom requis throws.

@FunctionalInterface
public interface ThrowingRunnable {
    void run() throws Throwable;
}

La méthode peut être utilisée comme suit:

class CustomException extends Exception {
    public final String message;
    public CustomException(final String message) { this.message = message;}
}
CustomException e = assertThrows(CustomException.class, () -> {
    throw new CustomException("Lorem Ipsum");
});
assertEquals("Lorem Ipsum", e.message);
0
heio