web-dev-qa-db-fra.com

Utilisation des correspondeurs junit @Rule, expectCause () et hamcrest

J'ai un contrôle:

@Rule
public ExpectedException thrown = ExpectedException.none();
...
@Test
public void testMethod()
{
    final String error = "error message";
    Throwable expectedCause = new IllegalStateException(error);
    thrown.expectCause(org.hamcrest.Matchers.<Throwable>equalTo(expectedCause));
    someServiceThatTrowsException.foo();
}

Lorsqu'il est exécuté via la méthode de test mvn, j'obtiens l'erreur suivante:

Java.lang.NoSuchMethodError: org.junit.rules.ExpectedException.expectCause (Lorg/hamcrest/Matcher;) V

Test compile bien.

S'il vous plaît, aidez-moi, vous ne pouvez pas comprendre comment tester la cause de l'exception?

22
Alexandr

Essayez de cette façon:

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

@Test public void testMethod() throws Throwable {
    final String error = "error message";
    Throwable expectedCause = new IllegalStateException(error);
    thrown.expectCause(IsEqual.equalTo(expectedCause));
    throw new RuntimeException(expectedCause);
}

Envisagez de ne pas vérifier la cause par des égaux, mais par IsInstanceOf et/ou comapring le message d'exception si nécessaire. En comparant la cause par égaux, vérifiez également le stacktrace, qui peut être plus que ce que vous souhaitez tester/vérifier. Comme ceci par exemple:

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

@Test public void testMethod() throws Throwable {
    final String error = "error message";
    thrown.expectCause(IsInstanceOf.<Throwable>instanceOf(IllegalStateException.class));
    thrown.expectMessage(error);
    throw new RuntimeException(new IllegalStateException(error));
}
17
Harmlezz

Vous pouvez utiliser un matcher personnalisé comme décrit ici ( http://www.javacodegeeks.com/2014/03/junit-expectedexception-rule-beyond-basics.html ) pour rechercher la cause d'une exception.

Matcher personnalisé

private static class CauseMatcher extends TypeSafeMatcher<Throwable> {

    private final Class<? extends Throwable> type;
    private final String expectedMessage;

    public CauseMatcher(Class<? extends Throwable> type, String expectedMessage) {
        this.type = type;
        this.expectedMessage = expectedMessage;
    }

    @Override
    protected boolean matchesSafely(Throwable item) {
        return item.getClass().isAssignableFrom(type)
                && item.getMessage().contains(expectedMessage);
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("expects type ")
                .appendValue(type)
                .appendText(" and a message ")
                .appendValue(expectedMessage);
    }
}

Cas de test

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

@Test
public void verifiesCauseTypeAndAMessage() {
    thrown.expect(RuntimeException.class);
    thrown.expectCause(new CauseMatcher(IllegalStateException.class, "Illegal state"));

    throw new RuntimeException("Runtime exception occurred",
            new IllegalStateException("Illegal state"));
}
15
lazlev

Un peu plus brièvement avec les importations statiques et en vérifiant à la fois la classe et le message de l'exception de cause: 

import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

@Test
public void testThatThrowsNiceExceptionWithCauseAndMessages(){

     expectedException.expect(RuntimeException.class );
     expectedException.expectMessage("Exception message");                                           
     expectedException.expectCause(allOf(instanceOf(IllegalStateException.class),
                                        hasProperty("message", is("Cause message"))) );

     throw new RuntimeException("Exception message", new IllegalStateException("Cause message"));
}

Vous pouvez même utiliser le matcher hasProperty pour affirmer des causes imbriquées ou pour tester la méthode "getLocalizedMessage". 

14
borjab

C'est un problème de version de JUnit.

ExpectedException.expectCause() est depuis 4.11 .

Aucune de ces méthodes dans 4.10 ou moins. 

Vous devez vous assurer que votre version d'exécution JUnit> = 4.11 est identique à votre version de compilation.

11

Pour résumer tout.

Avec JUnit 4 (hamcrest 1.3, et soyez prudent, JUnit 4 dépend de hamcrest-core qui n'inclut pas le package org.hamcrest.beans)

Donc, vous devez importer:

<dependency>
  <groupId>org.hamcrest</groupId>
  <artifactId>hamcrest-all</artifactId>
  <version>1.3</version>
  <scope>test</scope>
</dependency>

Code:

import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;

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

@Test
public void testThatThrowsNiceExceptionWithCauseAndMessages(){

  expectedException.expect(RuntimeException.class );
  expectedException.expectMessage("Exception message");                                           
  expectedException.expectCause(
    allOf(
      isA(IllegalStateException.class),
      hasProperty("message", is("Cause message"))
    )
  );

  throw 
    new RuntimeException("Exception message", 
      new IllegalStateException("Cause message"));
}
5
Gmugra

Normalement j'aime plus la construction suivante:

expectedException.expectCause(isA(NullPointerException.class));

4
wikier

Le quelconque (classe <T>) de hamcrest fonctionne très bien:

@Rule
public ExpectedException thrown = ExpectedException.none();
...
@Test
public void testMethod()
{
    thrown.expect(RuntimeException.class);
    thrown.expectCause(org.hamcrest.Matchers.any(IllegalStateException.class));
}
3
Ali Cheaito

Vous pouvez le faire entièrement avec les corrélateurs intégrés org.hamcrest.Matchers.instanceOf et org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage:

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.both;
import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage;

public class temp {
    @Rule
    public ExpectedException expectedException = ExpectedException.none();

    @Test
    public void youCannotDivideByZero() {
        expectedException.expect(RuntimeException.class);
        expectedException.expectMessage(equalTo("Division exception"));
        expectedException.expectCause(both(hasMessage(equalTo("/ by zero"))).and(instanceOf(ArithmeticException.class)));
        divide(1, 0);
    }

    private float divide(int first, int second) {
        try {
            return first / second;
        } catch(ArithmeticException e) {
            throw new RuntimeException("Division exception", e);
        }
    }
}
0
migwellian

Importation

<dependency>
  <groupId>it.ozimov</groupId>
  <artifactId>Java7-hamcrest-matchers</artifactId>
  <version>1.3.0</version>
  <scope>test</scope>
</dependency>

Et alors:

@Rule
public ExpectedException thrown = ExpectedException.none();
...
@Test
public void testMethod()
{
    final String errorMessage = "error message";
    Class<? extends Throwable> expectedCause = IllegalStateException.class;
    thrown.expectCause(ExpectedException.exceptionWithMessage(expectedCause, errorMessage));
    someServiceThatTrowsException.foo();
}

Cela fonctionne aussi avec le sous-type de la cause. Dans d'autres solutions, j'ai observé qu'ils acceptaient un supertype, ce qui est faux à mon avis.

Le message doit être égal ou contenu dans le message d'erreur de la cause.

0
JeanValjean