我是否应该始终使用Lambda表达式进行异常测试?

时间:2016-04-27 10:51:37

标签: java exception junit lambda java-8

我总是用注释测试我的异常。

@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;方法,所有测试都可以具有相同的结构,因为这一切都归结为对断言的调用。

除了这两点之外,对于这种新方式还有任何专业论据吗?对我来说,感觉它仍然优于注释,只要我只测试一个方法在一次测试中。

3 个答案:

答案 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;
}