我总是用注释测试我的异常。
@Test (expected = Exception.class)
public void test1() {
methodToTest() // throws an exception
}
我终于切换到了Java 8,我遇到了lambda Expressions。现在还有另一种方法可以获得理想的结果。
@Test
public void test2() {
assertThrows(Exception.class, () -> methodToTest());
}
public static <X extends Throwable> Throwable assertThrows(
final Class<X> exceptionClass, final Runnable block) {
try {
block.run();
} catch(Throwable ex) {
if (exceptionClass.isInstance(ex))
return ex;
}
fail("Failed to throw expected exception");
return null;
}
我理解,使用第二个版本,您可以更精确地检查单个方法,并且您不必担心单个测试中的其他方法也可能抛出预期的异常。此外,使用&#34; assertThrows&#34;方法,所有测试都可以具有相同的结构,因为这一切都归结为对断言的调用。
除了这两点之外,对于这种新方式还有任何专业论据吗?对我来说,感觉它仍然优于注释,只要我只测试一个方法在一次测试中。
答案 0 :(得分:3)
你错过了第三种方式,ExpectedException
jUnit规则:
public class SimpleExpectedExceptionTest {
@Rule
public ExpectedException thrown= ExpectedException.none();
@Test
public void myMethod_throws_no_exception_when_passed_greeting() {
fixture.myMethod("hello");
}
@Test
public void myMethod_throws_MyException_when_passed_farewell() {
thrown.expect(MyException.class);
fixture.myMethod("goodbye");
}
}
我发现这比@Test (expected = ...)
版本更清晰,因为期望更接近方法调用。
还有一个普通的旧Java版本,我们曾经用它来做:
try {
fixture.myMethod("should throw");
fail("Expected an exception");
} catch (MyException e) {
// expected
}
其中哪一个“更好”完全取决于背景。不要普遍采用一种。选择能够在给定情况下为您提供最清晰测试的那个。
当您开始以lambda为中心的样式编写非测试代码时,您可能会发现自己想要使用以lambda为中心的assertThrows()
。
答案 1 :(得分:2)
如果要测试的是抛出异常,则第一种语法更好。它是标准的,它简洁,它可以防止你一遍又一遍地编写同样丑陋的try-catch。
如果要声明抛出异常并且某些方法未被调用,则可能会使测试稍微复杂一些。在这种情况下,手动捕获异常是合理的。
@Test
public void test1() {
DBClient dbClient = spy(new DBClient());
try {
new Server().doRequest(new InvalidRequest(), dbClient);
fail("should have thrown");
} catch (InvalidRequestException e) {
verify(dbClient, times(0)).query(any(Query.class));
}
}
关于lambdas的使用更具体,取决于你。请注意,Runnable
无法抛出已检查的异常,因此您需要类似
@FunctionalInterface
public interface ThrowingRunnable<E extends Exception> {
void run() throws E;
}
答案 2 :(得分:1)
我认为这两种方法都没有问题 - 在风格问题上没有对错;只使用最适合每种情况的那种。我建议assertThrows还应检查不是预期类型的抛出异常,并且正如@Dici建议的那样,使用允许检查异常的函数接口:
public static <X extends Throwable> Throwable assertThrows(
final Class<X> exceptionClass, final CheckedRunnable block) {
try {
block.run();
} catch(Throwable ex) {
if (exceptionClass.isInstance(ex)) {
return ex;
} else {
throw new AssertionError("Unexpected exception was thrown", ex);
}
}
fail("Failed to throw expected exception");
}
@FunctionalInterface
public interface CheckedRunnable<R extends RuntimeException> {
void run () throws Exception;
}