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

时间:2014-03-12 08:50:45

标签: java unit-testing maven junit

我有一个测试:

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

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

  

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

测试编译好。

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

10 个答案:

答案 0 :(得分:17)

以这种方式尝试:

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

考虑不要通过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));
}

答案 1 :(得分:16)

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

自定义匹配器

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

测试用例

@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 :(得分:15)

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

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

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

答案 3 :(得分:11)

这是JUnit版本问题。

ExpectedException.expectCause()4.11起。

4.10或更低版本中没有此类方法。

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

答案 4 :(得分:5)

总结一切。

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

所以,你需要导入:

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

代码:

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 :(得分:4)

来自hamcrest的any(Class&lt; T&gt;)匹配器很好地工作:

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

答案 6 :(得分:4)

通常我更喜欢以下结构:

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

答案 7 :(得分:0)

导入

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

然后:

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

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

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

答案 8 :(得分:0)

您可以使用内置匹配器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);
        }
    }
}

答案 9 :(得分:0)

验证消息和引发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))
    // ...