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

Java 我是否应该始终使用Lambda表达式进行异常测试?,java,exception,junit,lambda,java-8,Java,Exception,Junit,Lambda,Java 8,我总是用注释测试我的异常 @Test (expected = Exception.class) public void test1() { methodToTest() // throws an exception } 我最终切换到Java8,遇到了lambda表达式。现在有另一个选项来获得所需的结果 @Test public void test2() { assertThrows(Exception.class, () -> methodToTest()); } public

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

@Test (expected = Exception.class)
public void test1() {
  methodToTest() // throws an exception
}
我最终切换到Java8,遇到了lambda表达式。现在有另一个选项来获得所需的结果

@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;
  }
@测试
公共无效测试2(){
assertThrows(Exception.class,()->methodToTest());
}
公共静态可丢弃资产(
最终类例外类,最终可运行块){
试一试{
block.run();
}捕获(可丢弃的ex){
if(异常类isInstance(ex))
退换货;
}
失败(“未能引发预期异常”);
返回null;
}
我理解,使用第二个版本,您可以更精确地检查单个方法,并且您不必担心单个测试中可能引发预期异常的其他方法。此外,使用“assertThrows”方法,所有测试都可以具有相同的结构,因为这一切都归结为对断言的调用


除了这两点,新方法还有什么支持的论据吗?对我来说,只要我只在一次测试中测试一个方法,就觉得使用注释还是比较好的。

如果你只想测试抛出一个异常,那么第一种语法更好。它是标准的,它是简洁的,它防止你写同样丑陋的尝试一遍又一遍

您可以有一个稍微复杂一点的测试,您希望断言抛出了异常,并且没有调用某个方法。在这种情况下,手动捕获异常是合理的

@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;
} 
@functioninterface
公共接口ThrowingRunnable{
void run()抛出E;
} 

您错过了第三条路,
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()

我认为这两种方法都没有问题——风格上没有对错;只需使用最适合每种情况的。我建议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;
}
公共静态可丢弃资产抛出(
最终类例外类,最终检查(不可命名块){
试一试{
block.run();
}捕获(可丢弃的ex){
if(异常类isInstance(ex)){
退换货;
}否则{
抛出新的断言错误(“抛出了意外异常”,ex);
}
}
失败(“未能引发预期异常”);
}
@功能接口
公共接口CheckedRunnable{
void run()抛出异常;
}

这个问题中的lambda表达式在哪里?“test2”将lambda表达式交给assertThrows方法,该方法作为可运行的方法处理。您是否在问为什么需要使用lambda表达式?或者您为什么要做所有这些而不是使用@Test(expected=Exception.class)注释?如果是后者,你几乎是自己回答的,这是2个优点。对不起,我用这个方法读得太快了,你能在调用抛出异常的方法后做些什么吗?使用第一种技术,你可以将你喜欢的任何代码放入抛出方法,但是当方法按预期抛出时,它不会执行。对于普通的旧Java版本,您当然可以在catch块中放入任何您喜欢的内容,比如关于异常的断言,或者在catch块之后放入关于之后其他事物状态的断言。这就是我所想的,那么与注释相比,它带来了什么呢?它更加冗长,并且做同样的事情。不是批评,只是询问。对我来说,使用
(expected=…)
断言位于一个意外的位置——在测试之前。此外,您还可以在
ExpectedException
中创建更详细的期望值,例如抛出的
。expectCause(…)
侧注:使用布尔值
HasSprown
和另一个断言,该断言为bonkers。只要把
fail(“应该抛出”)放在新服务器(…)行之后,就完成了。