Java JUnit:测试异常的另一种模式
我在一个项目中工作,其中包含许多嵌入错误代码的“BusinessException” 在每个异常单元测试中,我都必须重复这种模式来测试这些错误代码: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
@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();
}