使用JUnit测试预期异常

开发人员经常使用单元测试来验证的一段儿代码的操做,不少时候单元测试能够检查抛出预期异常( expected exceptions)的代码。在Java语言中,JUnit是一套标准的单元测试方案,它提供了不少验证抛出的异常的机制。本文就探讨一下他们的优势。

咱们拿下面的代码做为例子,写一个测试,确保canVote() 方法返回true或者false, 同时你也能写一个测试用来验证这个方法抛出的IllegalArgumentException异常。框架

public class Student {
  public boolean canVote(int age) {
      if (i<=0) throw new IllegalArgumentException("age should be +ve");
      if (i<18) return false;
      else return true;
  }
}

(Guava类库中提供了一个做参数检查的工具类--Preconditions类,也许这种方法可以更好的检查这样的参数,不过这个例子也可以检查)。函数

检查抛出的异常有三种方式,它们各自都有优缺点:工具

1.@Test(expected…)

@Test注解有一个可选的参数,"expected"容许你设置一个Throwable的子类。若是你想要验证上面的canVote()方法抛出预期的异常,咱们能够这样写:单元测试

@Test(expected = IllegalArgumentException.class)
public void canVote_throws_IllegalArgumentException_for_zero_age() {
    Student student = new Student();
    student.canVote(0);
}

简单明了,这个测试有一点偏差,由于异常会在方法的某个位置被抛出,但不必定在特定的某行。测试

2.ExpectedException

若是要使用JUnit框架中的ExpectedException类,须要声明ExpectedException异常。.net

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

而后你能够使用更加简单的方式验证预期的异常。code

@Test
public void canVote_throws_IllegalArgumentException_for_zero_age() {
    Student student = new Student();
    thrown.expect(NullPointerException.class);
    student.canVote(0);
}

或者能够设置预期异常的属性信息。开发

@Test
public void canVote_throws_IllegalArgumentException_for_zero_age() {
    Student student = new Student();
    thrown.expect(IllegalArgumentException.class);
    thrown.expectMessage("age should be +ve");
    student.canVote(0);
}

除了能够设置异常的属性信息以外,这种方法还有一个优势,它能够更加精确的找到异常抛出的位置。在上面的例子中,在构造函数中抛出的未预期的(unexpected) IllegalArgumentException 异常将会引发测试失败,咱们但愿它在canVote()方法中抛出。get

从另外一个方面来讲,若是不须要声明就更好了it

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

它就像不须要的噪音同样,若是这样就很好了

expect(RuntimeException.class)

或者:

expect(RuntimeException.class, “Expected exception message”)

或者至少能够将异常和信息当作参数传进去

thrown.expect(IllegalArgumentException.class, “age should be +ve”);

3.Try/catch with assert/fail

在JUnit4以前的版本中,使用try/catch语句块检查异常

@Test
public void canVote_throws_IllegalArgumentException_for_zero_age() {
    Student student = new Student();
    try {
        student.canVote(0);
    } catch (IllegalArgumentException ex) {
        assertThat(ex.getMessage(), containsString("age should be +ve"));
    }
    fail("expected IllegalArgumentException for non +ve age");
}

尽管这种方式很老了,不过仍是很是有效的。主要的缺点就是很容易忘记在catch语句块以后须要写fail()方法,若是预期异常没有抛出就会致使信息的误报。我曾经就犯过这样的错误。

总之,这三种方法均可以测试预期抛出的异常,各有优缺点。对于我我的而言,我会选择第二种方法,由于它能够很是精确、高效的测试异常信息。

相关文章
相关标签/搜索