使用junit @Rule,expectCause()和hamcrest匹配器

时间:2021-09-14 05:04:50

I have a test:

我有一个测试:

@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();
}

When run via mvn the test method, I'm getting the error:

当通过mvn运行测试方法时,我收到错误:

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

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

Test compiles fine.

测试编译好。

Please help me, cannot understand how to test the cause of the exception?

请帮帮我,不明白如何测试异常的原因?

9 个解决方案

#1


15  

You can use a custom matcher as described here (http://www.javacodegeeks.com/2014/03/junit-expectedexception-rule-beyond-basics.html) to test for the cause of an exception.

您可以使用此处所述的自定义匹配器(http://www.javacodegeeks.com/2014/03/junit-expectedexception-rule-beyond-basics.html)来测试异常的原因。

Custom matcher

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);
    }
}

Test case

@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"));
}

#2


14  

Try it this way:

试试这种方式:

@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);
}

Consider not to check against the cause by equals but by IsInstanceOf and / or comapring the exception message if necessary. Comparing the cause by equals check the stacktrace as well, which may be more than you would like to test / check. Like this for example:

考虑不要通过equals检查原因,而是通过IsInstanceOf检查和/或在必要时将异常消息组合在一起。通过equals比较原因也检查堆栈跟踪,这可能比您想要测试/检查的更多。像这样举例如:

@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));
}

#3


12  

A little bit more briefly with static imports and checking both the class and the message of the cause exception:

稍微简单介绍静态导入并检查原因异常的类和消息:

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"));
}

You could even use the hasProperty matcher to assert nested causes or to test the "getLocalizedMessage" method.

您甚至可以使用hasProperty匹配器来断言嵌套原因或测试“getLocalizedMessage”方法。

#4


11  

It's JUnit version problem.

这是JUnit版本问题。

ExpectedException.expectCause() is since 4.11.

ExpectedException.expectCause()从4.11开始。

No such method in 4.10 or lower.

在4.10或更低版本中没有这样的方法。

You should ensure your runtime JUnit version >= 4.11, same as your compile version.

您应确保运行时JUnit版本> = 4.11,与编译版本相同。

#5


4  

To summarize all.

总结一下。

With JUnit 4 ( hamcrest 1.3 , and be careful, JUnit 4 depend on hamcrest-core which not include org.hamcrest.beans package)

使用JUnit 4(hamcrest 1.3,小心,JUnit 4依赖于hamcrest-core,不包括org.hamcrest.beans包)

So, you need to import:

所以,你需要导入:

<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"));
}

#6


3  

The any(Class<T>) matcher from hamcrest works nicely:

来自hamcrest的any(Class )匹配器很好地工作:

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

#7


3  

Normally I like more the following construction:

通常我更喜欢以下结构:

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

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

#8


0  

Import

进口

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

And then:

接着:

@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();
}

It works also with subtype of the cause. In other solutions I observed that they accept a supertype, that is wrong in my opinion.

它也适用于原因的子类型。在其他解决方案中,我观察到他们接受超类型,我认为这是错误的。

Message must be equal or contained in the cause's error message.

消息必须相等或包含在原因的错误消息中。

#9


0  

You can do this entirely with built-in matchers org.hamcrest.Matchers.instanceOf and org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage:

您可以使用内置匹配器org.hamcrest.Matchers.instanceOf和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);
        }
    }
}

#1


15  

You can use a custom matcher as described here (http://www.javacodegeeks.com/2014/03/junit-expectedexception-rule-beyond-basics.html) to test for the cause of an exception.

您可以使用此处所述的自定义匹配器(http://www.javacodegeeks.com/2014/03/junit-expectedexception-rule-beyond-basics.html)来测试异常的原因。

Custom matcher

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);
    }
}

Test case

@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"));
}

#2


14  

Try it this way:

试试这种方式:

@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);
}

Consider not to check against the cause by equals but by IsInstanceOf and / or comapring the exception message if necessary. Comparing the cause by equals check the stacktrace as well, which may be more than you would like to test / check. Like this for example:

考虑不要通过equals检查原因,而是通过IsInstanceOf检查和/或在必要时将异常消息组合在一起。通过equals比较原因也检查堆栈跟踪,这可能比您想要测试/检查的更多。像这样举例如:

@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));
}

#3


12  

A little bit more briefly with static imports and checking both the class and the message of the cause exception:

稍微简单介绍静态导入并检查原因异常的类和消息:

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"));
}

You could even use the hasProperty matcher to assert nested causes or to test the "getLocalizedMessage" method.

您甚至可以使用hasProperty匹配器来断言嵌套原因或测试“getLocalizedMessage”方法。

#4


11  

It's JUnit version problem.

这是JUnit版本问题。

ExpectedException.expectCause() is since 4.11.

ExpectedException.expectCause()从4.11开始。

No such method in 4.10 or lower.

在4.10或更低版本中没有这样的方法。

You should ensure your runtime JUnit version >= 4.11, same as your compile version.

您应确保运行时JUnit版本> = 4.11,与编译版本相同。

#5


4  

To summarize all.

总结一下。

With JUnit 4 ( hamcrest 1.3 , and be careful, JUnit 4 depend on hamcrest-core which not include org.hamcrest.beans package)

使用JUnit 4(hamcrest 1.3,小心,JUnit 4依赖于hamcrest-core,不包括org.hamcrest.beans包)

So, you need to import:

所以,你需要导入:

<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"));
}

#6


3  

The any(Class<T>) matcher from hamcrest works nicely:

来自hamcrest的any(Class )匹配器很好地工作:

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

#7


3  

Normally I like more the following construction:

通常我更喜欢以下结构:

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

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

#8


0  

Import

进口

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

And then:

接着:

@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();
}

It works also with subtype of the cause. In other solutions I observed that they accept a supertype, that is wrong in my opinion.

它也适用于原因的子类型。在其他解决方案中,我观察到他们接受超类型,我认为这是错误的。

Message must be equal or contained in the cause's error message.

消息必须相等或包含在原因的错误消息中。

#9


0  

You can do this entirely with built-in matchers org.hamcrest.Matchers.instanceOf and org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage:

您可以使用内置匹配器org.hamcrest.Matchers.instanceOf和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);
        }
    }
}