tl; dr
JDK8之后:使用 AssertJ 或自定义lambda来声明 异常 行为。
JDK8之前的版本:我将推荐旧的好
try
-catch
块。( 不要忘记fail()
在该catch
块之前添加一个断言)
无论是Junit 4还是JUnit 5。
长话
可以自己编写自己 做的代码
try-
catch阻止或使用JUnit工具(
@Test(expected = ...)或
@RuleExpectedExceptionJUnit规则功能)。
但是这些方法并不那么优雅,并且在 可读性方面没有 与其他工具很好地融合在一起。而且,JUnit工具确实存在一些陷阱。
在
try
-catch
块,你必须写周围的测试行为块,写在catch块的断言,这可能是罚款,但很多人觉得这种风格中断测试的阅读流程。另外,您需要Assert.fail
在代码try
块的末尾写一个。否则,测试可能会遗漏断言的某一方面; PMD , findbugs 或 Sonar 会发现此类问题。该
@Test(expected = ...)
功能很有趣,因为您可以编写更少的代码,然后编写此测试的代码据说不那么容易出错。 但是 在某些领域缺少这种方法。- 如果测试需要检查有关异常的其他内容,例如原因或消息(好的异常消息非常重要,那么具有精确的异常类型可能还不够)。
- 同样,由于方法中的期望值很高,这取决于测试代码的编写方式,然后测试代码的错误部分会引发异常,从而导致测试结果呈假阳性,并且我不确定 PMD , findbugs 或 Sonar 将提供有关此类代码的提示。
@Test(expected = WantedException.class)
public void call2_should_throw_a_WantedException__not_call1() {
// init tested
tested.call1(); // may throw a WantedException// call to be actually testedtested.call2(); // the call that is supposed to raise an exception
}
该
ExpectedException
规则也是试图解决以前的警告,但是由于使用期望样式,使用它感觉有点尴尬, EasyMock 用户非常了解这种样式。对于某些人来说可能很方便,但是如果您遵循 行为驱动开发 (BDD)或“ 安排行为声明” (AAA)原则,则该ExpectedException
规则将不适合那些写作风格。除此之外,它可能会遇到与@Test
方式相同的问题,具体取决于您放置期望的位置。
@Rule ExpectedException thrown = ExpectedException.none() @Test public void call2_should_throw_a_WantedException__not_call1() { // expectations thrown.expect(WantedException.class); thrown.expectMessage("boom"); // init tested tested.call1(); // may throw a WantedException // call to be actually tested tested.call2(); // the call that is supposed to raise an exception }即使预期的异常位于测试语句之前,如果测试遵循BDD或AAA,它也会破坏您的阅读流程。
另外,请参见的作者关于JUnit的注释问题
ExpectedException。JUnit
4.13-beta-2甚至不赞成使用此机制:
拉取请求#1519:弃用ExpectedException
Assert.assertThrows方法提供了一种验证异常的更好方法。另外,与其他规则(如TestWatcher)一起使用时,ExpectedException的使用容易出错,因为在这种情况下,规则的顺序很重要。
因此,以上所有这些选项都有很多警告,并且显然无法避免编码错误。
- 创建这个答案后,我意识到一个项目看起来很有希望,那就是 catch-exception 。
正如该项目的描述所言,它使编码人员可以流畅地编写一行代码以捕获该异常,并为后者的断言提供此异常。而且,您可以使用任何断言库,例如Hamcrest或AssertJ。
来自主页的一个快速示例:
// given: an empty list List myList = new ArrayList(); // when: we try to get the first element of the list when(myList).get(1); // then: we expect an IndexOutOfBoundsException then(caughtException()) .isInstanceOf(IndexOutOfBoundsException.class) .hasMessage("Index: 1, Size: 0") .hasNoCause();如您所见,代码确实非常简单,您可以在特定行上捕获异常,该
thenAPI是将使用AssertJ API的别名(类似于using
assertThat(ex).hasNoCause()...)。 在某些时候,该项目依赖于FEST-声明AssertJ的祖先 。 编辑:
似乎该项目正在酝酿对Java 8 Lambdas的支持。
当前,该库有两个缺点:
* 在撰写本文时,值得注意的是,该库基于Mockito 1.x,因为它创建了幕后测试对象的模拟。由于Mockito仍未更新, **因此该库无法使用最终类或最终方法** 。即使它基于当前版本的Mockito 2,这也需要声明一个全局模拟制作器(`inline-mock-maker`),这可能不是您想要的,因为该模拟制作器与常规模拟制作器有不同的缺点。* 它需要另一个测试依赖项。
一旦库支持lambda,这些问题将不再适用。但是,该功能将由AssertJ工具集复制。
如果您不想使用catch-exception工具,请考虑所有因素,我将建议使用try
- catch
块的旧方法,至少可以使用JDK7。对于JDK
8用户,您可能更喜欢使用AssertJ,因为它提供的不仅仅是断言异常。
- 使用JDK8,lambda进入了测试环境,事实证明它们是断言异常行为的一种有趣方式。AssertJ已更新,提供了一个很好的流利API来声明异常行为。
以及使用AssertJ进行的样本测试:
@Test public void test_exception_approach_1() { ... assertThatExceptionOfType(IOException.class) .isThrownBy(() -> someBadIOOperation()) .withMessage("boom!"); } @Test public void test_exception_approach_2() { ... assertThatThrownBy(() -> someBadIOOperation()) .isInstanceOf(Exception.class) .hasMessageContaining("boom"); } @Test public void test_exception_approach_3() { ... // when Throwable thrown = catchThrowable(() -> someBadIOOperation()); // then assertThat(thrown).isInstanceOf(Exception.class) .hasMessageContaining("boom"); }- 通过对JUnit 5的近乎完全的重写,断言已得到了一些改进,作为断言正确地声明异常的开箱即用的方式,它们可能证明很有趣。但是实际上断言API仍然有点差,外面没有东西
assertThrows
。
@Test @DisplayName("throws EmptyStackException when peeked") void throwsExceptionWhenPeeked() { Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek()); Assertions.assertEquals("...", t.getMessage()); }如您所见,
assertEquals仍然在返回
void,因此不允许像AssertJ这样的链式断言。
另外,如果您还记得与
Matcher或发生冲突的名称
Assert,请准备与发生相同的冲突
Assertions。
我想得出一个结论:今天(2017-03-03)不论测试框架(JUnit )是什么, AssertJ 的易用性,可发现的API,快速的开发速度以及
事实上的 测试依赖性是使用JDK8的最佳解决方案。还是不可以),即使以前的JDK 感到笨拙,也应该改用 try
-catch
块。



