日暮途远

日暮途远,涸辙难行;东隅已逝,桑榆非晚

如何使用 JUnit 进行异常用例测试

异常情况是我们程序中非常常见的,同时,也是保系统正常异常的临界点。确保程序能够正常的运转很重要,但是要确保程序如期的按照逻辑抛出异常也非常的必要,例如参数的校验合法性问题,程序运行的超时问题等等。那我们如何在测试用例中来编写验证这些要抛出异常的逻辑呢?

在 JUnit3 的时代,你可能会写出如下的测试用例代码:

注意,try 中的 fail 语句,这个语句保证了如果系统未按预期进入 catch 块,测试用例就报告失败,如果不加,而且系统未抛出异常,则这个用例就会被认为是通过的,而不是你预期的抛出异常,起不到异常测试的作用。这个测试用例没问题,也能正常的保证我们的程序在该抛出异常的时候,能如期的表现出异常的行为。

但是在,JUnit4 的时代,我们可以做得更简洁明晰一些,同样的测试逻辑,用 JUnit4 编写的用例如下:

非常的简洁, 通过给 @Test 注解添加了 expected 属性来表明我预期要抛出的异常,如果这个用例抛出了 IndexOutOfBoundsException 那么本次的断言测试便成功通过,否则失败。该注解有2个属性可以用来描述我们的预期,除了刚刚的 expected 属性说明异常的类型外,还有一个 timeout 属性,可以用来定义超时的情况,如果用例运行超过了该指定的时间,则会被认为断言失败,这对一些耗时的任务测试非常有帮助。

但是,这个测试的方式仍然有它不足的地方:该方式只能测试用例是否抛出了预期的异常类型,并不能从其它的方面来更详细的区分定位异常。如果用例中应该要抛出2个该类型的异常,但是又是不同的(比如 message 部分不同),我们应该要怎样区分呢?如下场景:业务应用中,同一个用例抛出了以下2种异常:

我们要如何分别测试这2种情况呢?显然,上面的方式做不到,因为它只能细化到异常的类型,而无法分辨 message 信息。

这个时候,就是我们的 @Rule 和 ExpectedException 发挥作用的时候啦,准确的说这是 JUnit4.7 给出的新的特性,从命名上来看,它定义的是一个预期的异常,即表明我们的测试用例的初衷是要它抛出我想要的异常的。在测试方法体中已经不再需要 try,catch 的支持了,我们通过预期异常来说明我们想要的是什么类型的异常,异常的 message 应该是什么样的。当然,它还有很多其它的属性可以用来区分不同场景下的异常。于是,我们的测试用例的代码就可以优化成如下的格式了:

注意我们用 @Rule 定义的一个预期异常,准确的说这是 JUnit4.7 给出的新的特性,从命名上来看,它定义的是一个预期的异常,即表明我们的测试用例的初衷是要它抛出我想要的异常的。在测试方法体中已经不再需要 try,catch 的支持了,我们通过预期异常来说明我们想要的是什么类型的异常,异常的 message 应该是什么样的。

上面代码需重点关注几个:
1. @Rule 注解的  ExpectedException 变量声明,它必须为  public;
2. @Test 处,不能写成 @Test(expected=IndexOutOfBoundsException.class),否则不能正确测试,也就是 @Test(expected=IndexOutOfBoundsException.class) 和测试方法中的 expectedEx.expectXxx() 方法是不能同时并存的;
3. expectedEx.expectMessage() 中的参数是 Matcher 或  subString,就是说可用正则表达式判定,或判断是否包含某个子字符串;
4. 再就是有一点很重,把被测试方法写在 expectedEx.expectXxx() 方法后面,不然也不能正确测试的异常;
5. 最后一个是,只要测试方法直接抛出被测试方法的异常即可,并不影响你所关心的异常;

 

有同学可能会说,4.7 推出的这种异常测试方法也没什么神奇的,和我们最传统的 try,catch 方法相比,除了减少了我们编写 try,catch 的代码之外,也没什么其它的优势啊,而且我在 try,catch 里面可以做更多的事情,更详细的区分啊。是的,表面上看起来是的,但是简洁优雅的代码总是让人更喜爱的,另外,除了简洁与优雅,其它的真的是一样吗?建议这类同学赶紧去跑一下两种方法,看看输出,就知道新的方法的好处啦,卖个关子,有兴趣的同学自己去尝试吧,这样更能学到新的知识。

点赞

发表评论

电子邮件地址不会被公开。

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">