shine_cn

Your heart is free, so have the courage to follow it.
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

JUnit4 使用指南三 (Runner 特性分析)

Posted on 2011-05-28 18:23  shine_cn  阅读(1559)  评论(0编辑  收藏  举报
大家有没有想过这个问题:当你把测试代码提交给JUnit框架后,框架如何来运行你的代码呢?答案就是——Runner。在JUnit中有很多个Runner,他们负责调用你的测试代码,每一个Runner都有各自的特殊功能,你要根据需要选择不同的Runner来运行你的测试代码。可能你会觉得奇怪,前面我们写了那么多测试,并没有明确指定一个Runner啊?这是因为JUnit中有一个默认Runner,即BlockJUnit4ClassRunner,如果你没有指定,那么系统自动使用默认Runner来运行你的代码。
以第一章节的测试代码为例:
package com.rigel.ut;

import org.junit.Test;

public class CalculatorTest {

@Test
public void testAdd() {
// ...
}
}
这段代码相当于:
package com.rigel.ut;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.internal.runners.BlockJUnit4ClassRunner;

@RunWith(
BlockJUnit4ClassRunner.class)
public class CalculatorTest {

@Test
public void testAdd() {
// ...
}
}

一般情况下,默认测试运行器可以应对绝大多数的单元测试要求;当使用 JUnit 提供的一些高级特性(例如即将介绍的两个特性)或者针对特殊需求定制JUnit测试方式时,显式的声明测试运行器就必不可少了。在Junit4 中,Junit为我们定义好了一些运行器,他们的层次结构如下:
其中ParentRunner是一个抽象类,包含了一个Runner的List,可以负责多个Runner运行。Suite是 一个测试套,继承了ParentRunner。BlockJUnit4ClassRunner则是Juint4默认的运行器,叫成Block是因为他是按 顺序一个一个执行测试用例的,Junit4中没有提供实现多线程执行的运行器。Junit4这个运行器只是单纯继承了 BlockJUnit4ClassRunner没有对其进行更改,并且它是final类型的,不允许继续被继承。Enclosed是实现内部类的测试类的运行器。Parameterized则可以设置参数化的执行测试用例。JUnit38ClassRunner是为了向后兼容JUnit3而定义的运行器。你也可以继承上面的类来实现新的运行器和注解功能。下面我们主要对Parameterized及Suite Runner进行介绍。
1. Parameterized Runner 实现参数化测试
为了保证单元测试的严谨性,我们模拟了不同的测试数据来测试方法的处理能力,为此我们编写了大量的单元测试方法。这些测试方法都是大同小异:代码结构都是相同的,不同的仅仅是测试数据和期望值,为了解决这个问题,JUnit4提供了参数化测试。
举个简单的例子,比如我们需要对Calculator的加法功能进行测试,我们需要考虑“正数+正数”、“正数+负数”、“负数+负数”等等情况,因此我们可能需要像下面的代码那样写多个自己的测试方法:
package com.rigel.ut;

import org.junit.Assert;
import org.junit.Test;


public class AddTest {

private Calculator calc = new Calculator();

@Test
public void testAdd1() {
calc.add(
1,4);
int result = 5;
Assert.assertEquals(result, calc.getResult());
}

@Test
public void testAdd2() {
calc.add(
-1,-4);
int result = -5;
Assert.assertEquals(result, calc.getResult());
}

// any other test methods
// ...

}
这些测试方法内容相同,仅仅只是测试数据有差异而已,写很多Test方法,其实是比较让人纠结的。而JUnit4提出的“参数化测试”,可以只写一个测试函数,把这若干种情况作为参数传递进去,一次性的完成测试。代码实现如下:
package com.rigel.ut;

import java.util.Arrays;
import java.util.Collection;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.
class)
public class AddTest {

private int result;
private int param1;
private int param2;


/**
* 存放需要进行测试的数据集合
*
*/
@Parameters
@SuppressWarnings(
"rawtypes")
public static Collection prepareData() {
Object[][] data
= {{5, 1, 4}, {-3, 1, -4}, {-5, -1, -4}};

return Arrays.asList(data);
}

/**
* 对变量进行初始化
*
*/
public AddTest(int result, int param1, int param2) {
this.result = result;
this.param1 = param1;
this.param2 = param2;
}

@Test
public void addTest1() {
Calculator calc
= new Calculator();
calc.add(param1, param2);
Assert.assertEquals(result, calc.getResult());
}

}

代码的实现过程大致如下。首先,因为是采用了参数化测试,这时候TestCase所采用的Runner已经不再是系统默认的Runner,所 以,在类的定义处,必须通过@RunWith注解指定该TestCase采用的是Parameterized.class Runner;其次,因为需要设定你的测试数据集合,主要是通过@parameters注解标注的 prepareData 方法来实现的,其返回的便是一个包含测试数据及测试结果的数据集合;最后需要定义该测试类的参数初始化过程,即需要制定其定义的参数是如何与设定的测试数 据集合中的数据对应上。在此基础上,便可以利用定义的参数来写你的测试方法,这样只需要定义一次@Test注解的测试方法,就可以把所有的测试数据都跑一 遍了。其最后测试结果如下:
2. Suite Runner 实现打包测试
一般在一个项目中,只写一个测试类是不可能的,我们会写出很多很多个测试类。如何这些测试类必须一个一个的执行,也是非常麻烦的事情。鉴于此,JUnit为我们提供了打包测试的功能,将所有需要运行的测试类集中起来,一次性的运行完毕,这大大的方便了我们的测试工作。
为 了方便说明,我们分开在两个测试类 AddTest & SubstractTest中,分别测试Calculator的加法及减法功能,然后使用Suite Runner实现一次Run两个TestCase。其中AddTest源代码同上,而SubstractTest代码如下:
package com.rigel.ut;

import org.junit.Assert;
import org.junit.Test;

public class SubstractTest extends SupperTest{

private Calculator calc = new Calculator();

@Test
public void testSubstract() {
int result = 5;
calc.substract(
10, 5);
Assert.assertEquals(result, calc.getResult());
}

}
实现打包测试的TestCase取名“SuiteTest”,其代码实现如下:
package com.rigel.ut;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

@RunWith(Suite.
class)
@Suite.SuiteClasses({AddTest.
class, SubstractTest.class})
public class SuiteTest {

}

从“SuiteTest.java”源代码可以看出,该测试类没有任何实现,其只是通过@RunWith注解标识测试采用的Runner为 Suite Runner,另外通过@Suite.SuiteClasses注解将所有需要进行测试的类打包进来。最后SuiteTest的测试结果如下:
从图中可以很清晰的看到打包测试的具体情况。总结一下,本章节主要对JUnit4的Runner特性进行了简单的介绍,重点介绍了其中的参数化测试与打包测试的实现。本章节到此结束。谢谢。
PS: 三篇文档并非原创,算是自己看了些文档然后进行的整合吧,只是希望能把JUnit4的一些常见用法讲清楚,希望对大家有所帮助