JUnit4 使用进阶一中,我们介绍了JUnit4的下载安装,简单调用及运行测试方法,在本文中将继续对JUnit4提供的一些常用功能(忽略某个测试、对异常进行测试、设置超时时间、测试前后及顺序)进行介绍。

一、忽略某个测试

在测试过程中,我们可能需要临时禁止某个方法或者某个测试类的测试,比如:由于没完全准备好或者平台差异。这时,我们需要有一个方法可以告诉JUnit4框架,不要对这些方法或者类进行测试。

基于这个需要,JUnit4提供了一个注释@Ignore

举个例子:

@Ignore 
@Test 
public void something() { ...

@Ignore也可以添加一个可选的字符串参数,来说明为什么要忽略这个测试,如下:

@Ignore("还没准备好") 
@Test 
public void something() { ...

当然,@Ignore也可以直接作用在一个测试类上,如下:

@Ignore 
public class IgnoreMe {
	@Test 
	public void test1() { ... }
	
	@Test 
	public void test2() { ... }
}

二、对异常的测试

程序是否会按照我们所期待的,在运行过程中抛出异常呢?比如下面这个语句:

new ArrayList<Object>().get(0);

上面的代码将会抛出IndexOutOfBoundsException异常,@Test注释有一个可选参数expected,这个参数的取值是Throwable的子类。如果我们想判断上面的代码是否抛出正确的异常,测试代码可以这样写:

@Test(expected= IndexOutOfBoundsException.class) 
public void empty() { 
	new ArrayList<Object>().get(0); 
}

JUnit4框架会对上面expected参数值进行检查,被测试的方法中如果抛出IndexOutOfBoundsException异常,那么测试就通过了。

一般,对异常的简单的测试,使用上面的方法就够了,但是有时,我们需要检查异常所包含的提示信息,那么我们就需要使用ExpectedException规则,来帮助我们实现了。

看下面这个例子:

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

@Test
public void shouldTestExceptionMessage() throws IndexOutOfBoundsException {
    List<Object> list = new ArrayList<Object>();

    thrown.expect(IndexOutOfBoundsException.class);
    thrown.expectMessage("Index: 0, Size: 0");
    list.get(0); // execution will never get past this line
}

说明:

  1. 利用@Rule,我们可以对异常的提示信息进行检查。
  2. expectMessage方法还支持使用CoreMatchers.containsString来进行提示信息的匹配判断,如下:

     thrown.expectMessage(CoreMatchers.containsString("Size: 0"));
    

三、测试的超时时间

有时,我们需要控制程序的执行时间,当超出预设的超时时间,那么就判断测试失败。JUnit4框架也提供了这种检查,看下面例子:

@Test(timeout=1000)
public void testWithTimeout() {
  ...
}

通过@Test注释的一个可选参数timeout的数值(单位毫秒),我们可以告诉框架,预设的超时时间是多少。当测试运行中,执行时间超出了这个预设值,框架就会抛出TimeoutException异常,标记这个测试失败了。

我们也可以使用规则,来为整个测试类里面所有测试方法设置一个统一的超时时间,如下:

public class HasGlobalTimeout {
    public static String log;

    @Rule
    public Timeout globalTimeout = new Timeout(10000); // 10 seconds max per method tested

    @Test
    public void testInfiniteLoop1() {
        log += "ran1";
        for (;;) {
        }
    }

    @Test
    public void testInfiniteLoop2() {
        log += "ran2";
        for (;;) {
        }
    }
}

四、测试前后

每个测试之前,我们可能需要做一些数据准备操作,再每个测试之后,我们可能需要将测试数据进行恢复。那么,JUnit4框架如何提供什么样的方式,来实现我们的需求呢?

没错,JUnit4提供了四个注释,来标注测试类中的某些方法,是用来做测试前后的准备或者恢复操作的。这四个注释分别为:@Before@After@BeforeClass@AfterClass

看下面这个例子:

package test;

import java.io.Closeable;
import java.io.IOException;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class TestFixturesExample {
  static class ExpensiveManagedResource implements Closeable {
    @Override
    public void close() throws IOException {}
  }

  static class ManagedResource implements Closeable {
    @Override
    public void close() throws IOException {}
  }

  @BeforeClass
  public static void setUpClass() {
    System.out.println("@BeforeClass setUpClass");
    MyExpensiveManagedResource = new ExpensiveManagedResource();
  }

  @AfterClass
  public static void tearDownClass() throws IOException {
    System.out.println("@AfterClass tearDownClass");
    MyExpensiveManagedResource.close();
    MyExpensiveManagedResource = null;
  }

  private ManagedResource myManagedResource;
  private static ExpensiveManagedResource MyExpensiveManagedResource;

  private void println(String string) {
    System.out.println(string);
  }

  @Before
  public void setUp() {
    this.println("@Before setUp");
    this.myManagedResource = new ManagedResource();
  }

  @After
  public void tearDown() throws IOException {
    this.println("@After tearDown");
    this.myManagedResource.close();
    this.myManagedResource = null;
  }

  @Test
  public void test1() {
    this.println("@Test test1()");
  }

  @Test
  public void test2() {
    this.println("@Test test2()");
  }
}

这个例子的执行结果,如下:

@BeforeClass setUpClass
@Before setUp
@Test test2()
@After tearDown
@Before setUp
@Test test1()
@After tearDown
@AfterClass tearDownClass

说明:

  1. @Before@After定义的方法,会在每个测试方法运行的前后执行一遍;
  2. @BeforeClass@AfterClass定义的方法,会在整个测试类运行的开始和结束执行且仅执行一遍。
  3. 一般来说,@Before@After@BeforeClass@AfterClass提供的方法,是在某个测试类里面所使用的,无法被另外的测试类所公用,如果你想写一个类来处理数据,并被几个测试类所使用,那么你可以使用规则来实现。关于这一点,你可以看看我们后面的教程。

五、小结

在本文中,我们进一步介绍了JUnit4框架提供的一些功能,在继续的教程中,我们将介绍几个有特色的运行器,来提高测试效率。