junit:junit
JUnit测试中有多种处理异常的方法。 正如我在以前的一篇文章中所写的那样 ,我最好的方法是使用org.junit.rules.ExpectedException
规则。 基本上,规则是对org.junit.Before
, org.junit.After
, org.junit.BeforeClass
或org.junit.AfterClass
注释的方法的替代(或补充),但它们功能更强大,并且功能更多。在项目和课程之间轻松共享。 在本文中,我将展示org.junit.rules.ExpectedException
规则的更高级用法。
验证异常消息
标准JUnit的org.junit.Test
批注提供了expected
属性,该属性允许您指定Throwable
以在测试方法抛出指定类的异常时导致测试方法成功。 在许多情况下,这似乎足够了,但是如果您要验证异常消息,则必须找到其他方法。 使用ExpectedException
非常简单:
public class ExpectedExceptionsTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void verifiesTypeAndMessage() {
thrown.expect(RuntimeException.class);
thrown.expectMessage("Runtime exception occurred");
throw new RuntimeException("Runtime exception occurred");
}
}
在上面的代码片段中,我们希望消息contains
给定的子字符串。 仅与类型检查相比,它更安全。 为什么? 假设我们有一个ExceptionThrower
,如下所示:
class ExceptionsThrower {
void throwRuntimeException(int i) {
if (i <= 0) {
throw new RuntimeException("Illegal argument: i must be <= 0");
}
throw new RuntimeException("Runtime exception occurred");
}
}
如您所见,方法抛出的两个异常都是RuntimeException
,因此,如果我们不检查消息,则不能100%确定方法会抛出哪个异常。 以下测试将通过:
@Test
public void runtimeExceptionOccurs() {
thrown.expect(RuntimeException.class);
// opposite to expected
exceptionsThrower.throwRuntimeException(0);
}
@Test
public void illegalArgumentExceptionOccurs() {
thrown.expect(RuntimeException.class);
// opposite to expected
exceptionsThrower.throwRuntimeException(1);
}
检查异常中的消息将解决问题,并确保您正在验证所需的异常。 与@Test
注释方法的expected
属性相比,这已经是一个优势。
但是,如果您需要以更复杂的方式验证异常消息怎么办? ExpectedException
通过提供Hamcrest匹配器为expectMessage
方法(而不是字符串)允许您这样做。 让我们看下面的例子:
@Test
public void verifiesMessageStartsWith() {
thrown.expect(RuntimeException.class);
thrown.expectMessage(startsWith("Illegal argument:"));
throw new RuntimeException("Illegal argument: i must be <= 0");
}
如您所料,您可以提供自己的匹配器以验证消息。 让我们来看一个例子。
@Test
public void verifiesMessageMatchesPattern() {
thrown.expect(RuntimeException.class);
thrown.expectMessage(new MatchesPattern("[Ii]llegal .*"));
throw new RuntimeException("Illegal argument: i must be <= 0");
}
class MatchesPattern extends TypeSafeMatcher<String> {
private String pattern;
public MatchesPattern(String pattern) {
this.pattern = pattern;
}
@Override
protected boolean matchesSafely(String item) {
return item.matches(pattern);
}
@Override
public void describeTo(Description description) {
description.appendText("matches pattern ")
.appendValue(pattern);
}
@Override
protected void describeMismatchSafely(String item, Description mismatchDescription) {
mismatchDescription.appendText("does not match");
}
}
验证异常对象
在某些情况下,匹配消息可能还不够。 自定义方法可能会有例外,您也想对其进行验证。 没问题 ExpectedException
允许以expect
方法,它匹配。
@Test
public void verifiesCustomException() {
thrown.expect(RuntimeException.class);
thrown.expect(new ExceptionCodeMatches(1));
throw new CustomException(1);
}
class CustomException extends RuntimeException {
private final int code;
public CustomException(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}
class ExceptionCodeMatches extends TypeSafeMatcher<CustomException> {
private int code;
public ExceptionCodeMatches(int code) {
this.code = code;
}
@Override
protected boolean matchesSafely(CustomException item) {
return item.getCode() == code;
}
@Override
public void describeTo(Description description) {
description.appendText("expects code ")
.appendValue(code);
}
@Override
protected void describeMismatchSafely(CustomException item, Description mismatchDescription) {
mismatchDescription.appendText("was ")
.appendValue(item.getCode());
}
}
请注意,我同时实现了TypeSafeMatcher
describeTo
和describeMismatchSafely
方法。 当测试失败时,我需要它们产生看起来很漂亮的错误消息。 看下面的例子:
java.lang.AssertionError:
Expected: (an instance of java.lang.RuntimeException and expects code <1>)
but: expects code <1> was <2>
检查原因
您可以使用ExpectedException
做的另一件事是验证引发异常的原因。 这也可以使用自定义匹配器完成:
@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"));
}
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);
}
}
摘要
ExpectedException
规则是一个强大的功能。 通过添加Hamcrest
匹配器,您可以轻松地为异常测试创建健壮且可重用的代码。
- 可以在GitHub上找到代码示例。 还请检查我以前的文章: JUnit中处理异常的3种方法。 选择哪一个?
翻译自: https://www.javacodegeeks.com/2014/03/junit-expectedexception-rule-beyond-basics.html
junit:junit