Java JUnit:测试异常的另一种模式

Java JUnit:测试异常的另一种模式,java,unit-testing,design-patterns,exception-handling,junit,Java,Unit Testing,Design Patterns,Exception Handling,Junit,我在一个项目中工作,其中包含许多嵌入错误代码的“BusinessException” 在每个异常单元测试中,我都必须重复这种模式来测试这些错误代码: @Test public void zipFileReaderCtorShouldThrowAnExceptionWithInexistingArchive() { try { zfr = new ZipFileReader("unexpected/path/to/file"); fail("'Busines

我在一个项目中工作,其中包含许多嵌入错误代码的“BusinessException”

在每个异常单元测试中,我都必须重复这种模式来测试这些错误代码:

@Test
public void zipFileReaderCtorShouldThrowAnExceptionWithInexistingArchive() {
    try {
        zfr = new ZipFileReader("unexpected/path/to/file");
        fail("'BusinessZipException' not throwed");
    } catch (BusinessZipException e) {
        assertThat("Unexpected error code", e.getErrorCode(), is(ErrorCode.FILE_NOT_FOUND));
    } catch (Exception e) {
        fail("Unexpected Exception: '" + e + "', expected: 'BusinessZipException'");
    }
}
(由于错误代码测试,无法使用JUnit注释)

我对此感到厌烦,尤其是因为我必须在fail()的错误消息中复制/粘贴异常名称

因此,我编写了一个Util类。我使用一个抽象类来处理异常断言测试

public abstract class TestExceptionUtil { public void runAndExpectException(Class expectedException, String expectedErrorCode) { String failUnexpectedExceptionMessage = "Unexpected exception. Expected is: '%s', but got: '%s'"; try { codeToExecute(); fail("'" + expectedException.getName() + "' not throwed"); } catch (BusinessException e) { if (e.getClass().equals(expectedException)) { assertThat("Exception error code not expected", e.getErrorCode(), is(expectedErrorCode)); } else { fail(String.format(failUnexpectedExceptionMessage, expectedException.getName(), e)); } } catch (Exception e) { fail(String.format(failUnexpectedExceptionMessage, expectedException.getName(), e)); } } abstract public void codeToExecute(); } 你认为它“干净”吗?你认为可以改善吗?你认为它太重和/或没用了吗? 我的主要目标是在我们的开发团队中统一测试异常。(当然还有分解代码)

谢谢你的阅读

那JUnit呢

首先,在测试类的顶部声明
规则

@Rule
public final ExpectedException ee = ExpectedException.none();
然后,在您的测试方法中,您可以声明您可以预期出现
异常

@Test
public void testStuff() {
    ee.expect(IllegalArgumentException.class);
    ee.expectMessage("My Exception text");
}
我认为这比你的方法要干净得多

然后,您可以使用s来匹配
异常
消息:

@Test
public void testStuff() {
    ee.expect(IllegalArgumentException.class);
    ee.expectMessage(containsString("error"));
    ee.expect(hasProperty("errorCode", is(7)));
}
hasProperty
Matcher
将查找命名属性的getter,并检查它是否与第二个参数匹配,第二个参数是另一个
Matcher

您甚至可以实现自己的
匹配器
,在这种情况下,您不需要依赖hamcrest:

public class ErrorCodeMatcher extends BaseMatcher<Throwable> {

    private final int expectedErrorCode;

    public ErrorCodeMatcher(int expectedErrorCode) {
        this.expectedErrorCode = expectedErrorCode;
    }

    @Override
    public boolean matches(Object o) {
        return ((BusinessZipException) o).getErrorCode() == expectedErrorCode;
    }

    @Override
    public void describeTo(Description d) {
        d.appendText("Expected error code was" + expectedErrorCode);
    }
}
使用
静态
工厂方法和
静态
导入,这可以变得非常干净:

ee.expect(exceptionWithErrorCode(7));
如果您有一个通用的
接口
,它使用
getErrorCode()
方法定义业务
异常
,比如调用
ErrorAwareException
,那么您可以扩展
TypeSafeMatcher
类来创建稍微干净的代码:

public class ErrorCodeMatcher<T extends Exception & ErrorAwareException> extends TypeSafeMatcher<T> {

    public static <E extends Exception & ErrorAwareException> ErrorCodeMatcher<E> exceptionWithErrorCode(final int expectedErrorCode) {
        return new ErrorCodeMatcher<E>(expectedErrorCode);
    }
    private final int expectedErrorCode;

    public ErrorCodeMatcher(int expectedErrorCode) {
        this.expectedErrorCode = expectedErrorCode;
    }

    @Override
    protected boolean matchesSafely(final T t) {
        return t.getErrorCode() == expectedErrorCode;
    }

    @Override
    public void describeTo(Description d) {
        d.appendText("Expected error code was" + expectedErrorCode);
    }
}

我认为你实际上是在重新发明轮子。您可以使用
@Test
注释的
expected
参数,该注释导致在抛出给定异常时测试方法成功。或者使用
ExpectedException
规则基本相同,但功能更多。那就试试吧

@Test(expected = Exception.class)
public void myTest() {
    throw new Exception();
}


OP需要的不仅仅是通过
匹配异常-这就是为什么OP认为求助于homebrew solution.yep junit规则是测试异常的最佳方法。更多信息请点击这里
public class ErrorCodeMatcher<T extends Exception & ErrorAwareException> extends TypeSafeMatcher<T> {

    public static <E extends Exception & ErrorAwareException> ErrorCodeMatcher<E> exceptionWithErrorCode(final int expectedErrorCode) {
        return new ErrorCodeMatcher<E>(expectedErrorCode);
    }
    private final int expectedErrorCode;

    public ErrorCodeMatcher(int expectedErrorCode) {
        this.expectedErrorCode = expectedErrorCode;
    }

    @Override
    protected boolean matchesSafely(final T t) {
        return t.getErrorCode() == expectedErrorCode;
    }

    @Override
    public void describeTo(Description d) {
        d.appendText("Expected error code was" + expectedErrorCode);
    }
}
@Test(expected = Exception.class)
public void myTest() {
    throw new Exception();
}
@Rule
private ExpectedException rule = ExpectedException.none();

@Test
public void myTest() {
    rule.expect(Exception.class);
    throw new Exception();
}