junit:junit_JUnit ExpectedException规则:超越基础

junit:junit

JUnit测试中有多种处理异常的方法。 正如我在以前的一篇文章中所写的那样 ,我最好的方法是使用org.junit.rules.ExpectedException规则。 基本上,规则是对org.junit.Beforeorg.junit.Afterorg.junit.BeforeClassorg.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 describeTodescribeMismatchSafely方法。 当测试失败时,我需要它们产生看起来很漂亮的错误消息。 看下面的例子:

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匹配器,您可以轻松地为异常测试创建健壮且可重用的代码。


翻译自: https://www.javacodegeeks.com/2014/03/junit-expectedexception-rule-beyond-basics.html

junit:junit

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值