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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22346275/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 15:05:34  来源:igfitidea点击:

Using junit @Rule, expectCause() and hamcrest matchers

javaunit-testingmavenjunit

提问by Alexandr

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?

请帮助我,无法理解如何测试异常的原因?

回答by Harmlezz

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 和/或在必要时对异常消息进行 comapring。通过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));
}

回答by u290629

It's JUnit version problem.

这是 JUnit 版本问题。

ExpectedException.expectCause()is since 4.11.

ExpectedException.expectCause()4.11 开始

No such method in 4.10or lower.

4.10或更低版本中没有这种方法。

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

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

回答by jansohn

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

回答by borjab

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”方法。

回答by Ali Cheaito

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

来自 hamcrest 的 any(Class<T>) 匹配器效果很好:

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

回答by wikier

Normally I like more the following construction:

通常我更喜欢以下结构:

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

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

回答by JeanValjean

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.

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

回答by Gmugra

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 依赖于不包含 org.hamcrest.beans 包的 hamcrest-core)

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

回答by migwellian

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

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

回答by naXa

Example of verifying the message and cause of a thrown exception for Kotlin language:

验证 Kotlin 语言抛出异常的消息和原因的示例:

@get:Rule
val exceptionRule: ExpectedException = ExpectedException.none()

@Test
fun `test method`() {
    exceptionRule.expect(NestedServletException::class.java)
    exceptionRule.expectMessage("error msg")
    exceptionRule.expectCause(instanceOf(IllegalStateException::class.java))
    // ...