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

我有一个测试:

@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.equalTo(expectedCause)); someServiceThatTrowsException.foo(); } 

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

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

测试编译好。

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

试试这种方式:

 @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检查和/或在必要时将exception消息组合在一起。 通过equals比较原因也检查堆栈跟踪,这可能比您想要测试/检查的更多。 像这样举例如:

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

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

自定义匹配器

 private static class CauseMatcher extends TypeSafeMatcher { private final Class type; private final String expectedMessage; public CauseMatcher(Class 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")); } 

稍微简单介绍静态导入并检查原因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")); } 

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

这是JUnit版本问题。

ExpectedException.expectCause()从4.11开始 。

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

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

总结一下。

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

所以,你需要导入:

  org.hamcrest hamcrest-all 1.3 test  

码:

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

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

通常我更喜欢以下结构:

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

import

  it.ozimov java7-hamcrest-matchers 1.3.0 test  

然后:

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

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

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

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