2016-04-27 43 views
2

我总是用注释测试我的异常。我应该总是使用Lambda表达式进行异常测试吗?

@Test (expected = Exception.class) 
public void test1() { 
    methodToTest() // throws an exception 
} 

我终于切换到了Java 8,并且遇到了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; 
    } 

据我所知,与第二版本,你可以更精确地检查单的方法,你不必担心单个测试中的其他方法可能抛出预期的异常也是如此。此外,使用“assertThrows”方法,所有测试都可以具有相同的结构,因为这一切都归结为调用断言。

除了这两点,有没有什么有利于新方法的论点?对我来说,只要我只在单个测试中测试单一方法,感觉它仍然优于注释。

+0

凡在这个lambda表达式题 ? – Dici

+0

“test2”将lambda表达式移交给assertThrows方法,该方法作为Runnable进行处理。 – leimooo

+1

你问为什么有必要使用lambda表达式?或者为什么你会这样做,而不是使用@Test(expected = Exception.class)注释?如果是后者,你自己几乎可以回答这些问题,这些都是2点好处。 –

回答

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()

+0

用这种方法,你可以在调用抛出异常的方法之后做些什么吗? – Dici

+0

使用第一种技巧,您可以将任何您喜欢的代码放入throwing方法中,但当方法按预期抛出时,它不会执行。 使用普通的旧Java版本,当然你可以在catch块中放入任何你喜欢的东西,比如关于异常的断言,或者在catch块之后,就像之后关于其他事情状态的断言。 – slim

+0

这就是我的想法,那么与注释相比,它带来了什么?它更冗长,并做同样的事情。不要批评,只是问。 – Dici

2

如果您想要测试的是引发异常,则第一种语法更好。它是标准的,它是简洁的,它可以防止你一遍又一遍地写同样难看的尝试。

如果您想断言发生异常,您可以进行稍微复杂的测试某些方法未被调用。在这种情况下,手动捕获异常是合理的。

@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)); 
    } 
} 

关于lambda更具体的使用,取决于您。不过请注意Runnable不能抛出checked异常,所以你就需要像

@FunctionalInterface 
public interface ThrowingRunnable<E extends Exception> { 
    void run() throws E; 
} 
+1

注意:使用布尔'hasThrown'和另一个断言就是疯狂。在你的'新的服务器(...)'行后面加上'fail(“应该抛出”),你就完成了。 – GhostCat

+0

确实,我会编辑 – Dici

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