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.
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
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
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.
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.
dans junit, il y a quatre façons de tester l'exception.
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());
}
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
pour plus d'informations, vous pouvez lire ce document et guide de l'utilisateur junit5 pour plus de détails.
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.
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.
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.
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
}
_
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.
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.
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");
}
_
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
-catch
blocs 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.} _
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:
throws
Une méthode similaire sera ajoutée à org.junit Assert
dans JUnit 4.13.
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);
}
@Test
public void testFooThrowsIndexOutOfBoundsException() {
when(foo).doStuff();
then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);
}
eu.codearte.catch-exception:catch-exception:1.3.3
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");
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.
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 try
catch
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 .
Vous pouvez aussi faire ceci:
@Test
public void testFooThrowsIndexOutOfBoundsException() {
try {
foo.doStuff();
assert false;
} catch (IndexOutOfBoundsException e) {
assert true;
}
}
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.
@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
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.
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("");
}
}
Si vous souhaitez une solution qui:
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.
}
Utilisez-le comme suit:
@Test
public void testThrows()
{
RuntimeException e = expectException( RuntimeException.class, () ->
{
throw new RuntimeException( "fail!" );
} );
assert e.getMessage().equals( "fail!" );
}
JUnit a un support intégré pour cela, avec un attribut "attendu"
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));
}
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.
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");
}
}
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();
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());
}
En plus de ce que NamShubWriter a dit, assurez-vous que:
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.
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(".......);
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)
.
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"});
}
@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.
Il y a deux façons d'écrire un cas de test
@Test(expected = IndexOutOfBoundsException.class)
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 ...
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:
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);