air*_*ump 1 java junit unit-testing design-patterns exception-handling
我在一个项目中工作,其中包含许多嵌入了errorCode的"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'");
}
}
Run Code Online (Sandbox Code Playgroud)
(由于错误代码测试,使用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();
}
然后,客户端以这种方式使用它:
@Test
public void zipFileReaderCtorShouldThrowAnExceptionWithInexistingArchive() {
new TestExceptionUtil() {
@Override
public void codeToExecute() {
zfr = new ZipFileReader("unexpected/path/to/file");
}
}.runAndExpectException(BusinessTechnicalException.class, ErrorCode.FILE_NOT_FOUND);
}
Run Code Online (Sandbox Code Playgroud)
你觉得它"干净"吗?你认为它可以改善吗?你认为它太沉重和/或没用吗?我的主要目标是在开发团队中统一测试异常.(当然还有代码化代码)
谢谢阅读!
JUnit 怎么样?ExpectedException Rule
首先,您Rule在测试类的顶部声明:
@Rule
public final ExpectedException ee = ExpectedException.none();
Run Code Online (Sandbox Code Playgroud)
然后在您的测试方法中,您可以声明您可以期望Exception:
@Test
public void testStuff() {
ee.expect(IllegalArgumentException.class);
ee.expectMessage("My Exception text");
}
Run Code Online (Sandbox Code Playgroud)
我认为这比你的方法更清晰.
然后,您可以使用hamcrest Matcher来匹配Exception消息:
@Test
public void testStuff() {
ee.expect(IllegalArgumentException.class);
ee.expectMessage(containsString("error"));
ee.expect(hasProperty("errorCode", is(7)));
}
Run Code Online (Sandbox Code Playgroud)
该hasProperty Matcher会寻找已命名的属性getter和检查它的第二个参数匹配-这是另一种Matcher.
你甚至可以实现自己的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);
}
}
Run Code Online (Sandbox Code Playgroud)
这将使用如下:
ee.expect(new ErrorCodeMatcher(7));
Run Code Online (Sandbox Code Playgroud)
使用static工厂方法和static导入,这可以变得非常干净:
ee.expect(exceptionWithErrorCode(7));
Run Code Online (Sandbox Code Playgroud)
如果您有一个使用方法interface定义业务的公共事件,比如调用那么您可以扩展该类以创建稍微更清晰的代码:ExceptiongetErrorCode()ErrorAwareExceptionTypeSafeMatcher<T>
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);
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,如果您确实选择使用hamcrest,那么请确保在项目中包含junit-dep而不是pure junit,否则hamcrest类将与包含的hamcrest类冲突junit.在maven中,这看起来像这样:
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1751 次 |
| 最近记录: |