JUnit 4 如何正确测试异常

本篇讲述如何在 JUnit 4 下正确测试异常,我会从 try..catch 的方式谈起,然后说到 @Test(expected=Exception.class), 最后论及 @Rules public ExpectedException 的实现方式,最终基本可确定用 @Rules 是最方便的。

我们在用 JUnit 测试方法异常的时候,最容易想到的办法就是用 try...catch 去捕获异常,需要断言以下几个条件:

1. 确实抛出的异常
2. 抛出异常的 Class 类型
3. 抛出异常的具体类型,一般检查异常的 message 属性中包含的字符串的断定

所以常用的代码你可能会这么写:

@Test
public void passwordLengthLessThan6LettersThrowsException(){
    try{
        Password.validate("123");
        fail("No exception thrown.");
    }catch(Exception ex){
        assertTrue(ex instanceof InvalidPasswordException);
        assertTrue(ex.getMessage().contains("contains at least 6"));
    }
}

这里被测试的方法是 Password.validate() 方法是否抛出了相应的异常,注意这里别漏 try 中的

fail("No Exception thrown.")

代码行,不然如果被测试的方法如果没有抛出异常的话,这个用例是通过的,而你预期的是要抛出异常的。

上面的土办法对于哪个 JUnit 版本是是适合,可是我们早已步入了 JUnit 4 的时候,大可不必如此这般的去测试方法异常。虽然这样也能测定出是否执行出预期的异常来,但它仍有弊端,接下来会一对比就知道了,try...catch 的方法,JUnit 无法为你提示出详细的断言失败原因。

那么来看看自从 JUnit 4 后可以怎么去测试异常呢?用 @Test(execpted=Exception.class) 注解就行,参考如下代码:

@Test(expected = NullPointerException.class)
public void passwordIsNullThrowsException() throws InvalidPasswordException {
    Password.validate(null);
}

如果被测试的方法有抛出 NullPointerException 类型便是断言成功,对了 @Test(expected = NullPointerException.class) 只能判断出异常的类型,并无相应的注解能断言出异常的更具体的信息,即无法判定抛出异常的  message 属性。

那么,有时候我们会在一个方法中多次抛出一种类型的异常,但原因不同,即异常的 message 信息不同,比如出现 InvalidPasswordException 时会有以下两种异常:

new InvalidPasswordException("Password must contains at least 6 letters.")
new InvalidPasswordException("Password length less than 15 letters")

这就要有办法去断言异常的 message 了,针对于此,自 JUnit 4.7 之后又给了我们更完美的选择,就是下面的代码:

@Rule
public ExpectedException expectedEx = ExpectedException.none();

@Test
public void passwordIsEmptyThrowsException() throws InvalidPasswordException {
    expectedEx.expect(InvalidPassrdException.class);
    expectedEx.expectMessage("required");
    Password.validate("");
}

上面代码需重点关注几个:

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

前面说到用 try...catch 的办法也能正确测试到异常,@Test(expected=...) 或 @Rule 与 try...catch 的方法对比有什么好处呢,显然用 JUnit 4 推荐的方法简洁明了。再来看测试失败时 JUnit 会为你提示什么呢?

try...catch 测试异常失败时,得到的提示:

无异常时:

java.lang.AssertionError: No exception thrown.
    at org.junit.Assert.fail(Assert.java:91)
    at cc.unmi.PasswordTest.passwordLengthLessThan6LettersThrowsException(PasswordTest.java:20)

异常类型不对或异常的 message 不对时:

java.lang.AssertionError: 
    at org.junit.Assert.fail(Assert.java:91)
    at org.junit.Assert.assertTrue(Assert.java:43)
    at org.junit.Assert.assertTrue(Assert.java:54)
    at cc.unmi.PasswordTest.passwordLengthLessThan6LettersThrowsException(PasswordTest.java:22)

上面能提供给我们的定位错误的帮助不是特别大

再看 @Test(expected=InvalidPasswordException.class) 时测试失败时的提示:

java.lang.AssertionError: Expected exception: cc.unmi.InvalidPasswordException
    at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:32)
    at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:110)

用 @Rules ExpectedException方式来测试异常,失败时的提示:

java.lang.AssertionError: 
Expected: (exception with message a string containing "YES. required" and an instance of java.lang.NullPointerException)
     got: <cc.unmi.InvalidPasswordException: Password is required.>

at org.junit.Assert.assertThat(Assert.java:778)
    at org.junit.Assert.assertThat(Assert.java:736)
    at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:114)

特别是 @Rules  ExpectedException 方法时为何测试失败提示的清清楚楚。期望什么异常,异常 message 中含何字符串,实际上确得到什么类型的异常,异常中 message 是什么。有了这,你一看到就知道怎么去修补你的程序。

所以到了这里,我们真的没理由不用 @Test(expected=...) 或  @Rule ExpectedException 的写法了。

时间: 2024-10-11 15:19:36

JUnit 4 如何正确测试异常的相关文章

junit4 测试异常

在某些情况下,我们断定目标方法会抛出异常,这时该如何处理呢. 使用junit测试抛出的异常,我总结了3种方式,详见代码: import static org.junit.Assert.fail; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; /**  * 测试异常  *   * @author wei.ss  *   */ public class Exception

junit基础学习之-测试service层(3)

测试步骤: 在之前的文章中已经加了junit的环境,这就不需要了. 1.加载junit类,spring配置文件,指明junit测试器,@Runwith 2.定义变量,service,不可以使用spring注解,因为spring注解是建立在server上的. 3.初始化@Before注解. 4.实现测试方法,@Test注解. 1 package swust.edu.cn.postdoctors.service.impl; 2 3 import javax.annotation.Resource;

spring集成Junit做单元测试及常见异常解决办法

spring-test依赖包 <!--Spring-test --> <!-- https://mvnrepository.com/artifact/org.springframework/spring-test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version&g

Junit单元测试对线程测试没反应

今天还想简单写写线程的东西,结果刚刚写完我就懵逼了. 结果啥都没有输出,而用主方法运行就可以输出.没办法,只有度年来寻求自己的答案了 结果各方面的了解,我算是有个了解了. 首先来看看代码 package com.mendale.Test; import java.util.Date; import org.junit.Test; public class Test1 { @Test public void test() { MyTime mt = new MyTime(); mt.start()

junit基础学习之-测试controller层(2)

准备工作: eclipse本身带有junit4,可以直接build path,加入junit. 连接数据库的配置文件需要修改,之前的文件是采用properties+xml文件的形式,但是在测试的时候因为不能连接服务器,所以需要需要修改配置文件,最好是重新写一个配置文件,把需要的参数直接写出来就好了. 下面是我配置文件,重点是前面,后面就不需要了. <?xml version="1.0" encoding="UTF-8"?> <beans xmlns

类的继承与super()的意义以即如何写一个正确的异常类

这些东西都是我看了许多名师课程和自己研究的成果,严禁转载,这里指出了如何正确的自己定义一个异常类并看一看sun写的java的源代码话题一:子类的构造器执行是否一定会伴随着父类的构造执行? 1.this()与super()不能共存2.如果不显示地调用this()和super();子类对象的创建是否一定执行父类的构造3.既然super()和this()不能共存,又说子类的构造执行一定会执行父类的构造,那么我让子类的构造执行this()是不是就不能在执行父类的构造? 4.如果我非不让父类的构造执行,我

JUnit实战(1) - JUnit起步(Parameterized参数化测试)

创建Java Project项目,项目名称:ch01-jumpstart Calculator.java public class Calculator { public double add(double number1, double number2) { return number1 + number2; } } CalculatorTest.java import static org.junit.Assert.*; import junit.framework.Assert; impo

junit的使用,测试 calculate.java

0.juint的安装 下载junit的jar包,在要使用Junit的project名字上,点击properties--〉java build path-libraries,点击Add External JARs,把Junit包点上. 1.在eclipse中建立工程及calculate.java. 2.工程中建立calculateTest.java类. 3.建立过程 4.勾选要测试的类 5.生成的代码 6.添加测试代码 7.运行 8.结果

IDEA 整合Junit实现自动生成测试代码

1.安装插件 junit generator 重启IDEA,完成安装. 2. 选中需要测试的方法,按alt + insert 即可自动生成测试类\方法 3. 设置