在eclipse开发工具中使用JUnit4进行单元测试详解(三)高级篇

本文是在前人的基础上完善而成http://blog.csdn.net/andycpp/article/details/1329218

通过前两篇文章我们成功的在eclipse开发工具中生成了一个JUnit4测试框架,并且对测试框架的每个细节有了更深入的了解。下面我们探讨一下JUnit4中的一些高级特性。

首先,在我们的工作目录(src)中新建一个类Counter,代码如下:

package com.edward.junit;

public class Counter {
	private static int result = 10;//公共静态变量,用来存放计算结果
	public void addSelf(){//自加
		result ++;
	}
	public void substractSelf(){//自减
		result --;
	}
	public void multiplyCounter(int x){//乘以一个数
		result = result*x;
	}
	public void divideCounter(int x) throws ArithmeticException{//除以一个数
		if(x==0){
			throw new ArithmeticException("除数不能为0");
		}
		result = result/x;
	}
	public void squareRoot(int n){
		for(;;){}
	}
	public void equalTen(int x){//判断一个数是否等于10
		if(x>10){
			result = 1;
		}else if(x == 10){
			result = 0;
		}else{
			result = -1;
		}
	}
	public void resetResult(){//重置计算结果
		result = 10;
	}
	
	public int getResult(){//获取计算结果
		return result;
	}
	public void initResult()//初始化结果
	{
	   //此方法暂未写好
	}
}
重复入门篇中的第三步,选择待测方法如下图:


点击Finish后,生成一个JUnit测试类CounterTest.项目目录如下图:

完成测试类CounterTest中的代码,如下:

package com.edward.junit;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

public class CounterTest {
	private Counter counter = new Counter();//创建并初始化待测类对象
	@Before
	public void setUp() throws Exception {
		counter.resetResult();//在执行每个测试方法之前,重置计算结果result
	}

	@Test
	public void testAddSelf() {//测试自加
		counter.addSelf();
		assertEquals(11,counter.getResult());
	}

	@Test
	public void testSubstractSelf() {//测试自减
		counter.substractSelf();
		assertEquals(9,counter.getResult());
	}

	@Test
	public void testMultiplyCounter() {//测试与某数相乘
		counter.multiplyCounter(3);
		assertEquals(30,counter.getResult());
	}

	@Test
	public void testDivideCounter() {//测试除以一个数
		counter.divideCounter(2);
		assertEquals(5,counter.getResult());
	}
	@Ignore//忽略本测试
	@Test(timeout=1000)//进行耗时测试限制时间是1000
	public void testSquareRoot() {
		counter.squareRoot(16);
		assertEquals(4,counter.getResult());
	}
	@Ignore//忽略测试未实现的待测方法
	@Test
	public void testInitResult() {
		fail("Not yet implemented");
	}

}

重复入门篇中的第四步,运行结果如下:

其中:2skipped标明本次测试跳过了2个待测方法,测试类中这两个方法用@Ignore进行了标记。

下面我们来探讨一下本次测试的细节:

一、@Ignore标注,忽略测试某些尚未完成的待测方法。

如果在写程序之前做了很好的规划,哪些方法具体完成什么样的功能都已经确定(极限编程的思想),因此我们可以为它提前编写测试用例。但是如果写完了测试用例而待测方法尚未完成,那么测试结果一定是失败的。这种失败与程序中出现bug的失败是有区别的,因此为避免这种失败对正常的程序测试造成干扰,JUnit专门提供了一个方法来区别它们,那就是用@Ignore在测试方法前面进行标注,来忽略测试某些尚未完成的待测方法。这样测试结果中,就会提示有几个测试被忽略,而不是测试失败。等到待测方法完成后,只需要把@Ignore标注删去,就可以对本方法进行正常的测试。

二、JUnit4中的固定代码段Fixture

在JUnit测试中,存在着一些在某些阶段必然被调用的代码,比如在测试类中都要创建并初始化待测类的对象,在本次测试中,执行每次测试之前都要重置计算结果result,避免执行完一个测试方法后改变了result的值,再执行其它测试方法时,对执行结果产生影响。再进行测试时我们要确保每一个测试方法的独立性,相互之间没有任何耦合度,避免测试方法之间相互干扰,因此,我们在执行每个测试方法之前需要对测试初始条件进行初始化,在执行完之后,需要对占用的资源进行释放,所以,JUnit4为我们提供了@Before和@After标注方式。

    @Before

         标明在每个测试方法执行之前都要执行的方法

    @After

         标明在每个测试方法执行之后都要执行的方法

注:@Before和@After标明的方法在同一个类中只能各有一个,相当于之前版本中的setUp( )和tearDown( )方法,只是不再要求方法名必须这么命名。

再进行测试时,有时我们会遇到,在本类中所有的测试执行之前,需要执行的代码,或所有测试执行之后,需要执行的代码。例如:有一个类是负责对大文件(超过 500 兆)进行读写,他的每一个方法都是对文件进行操作。换句话说,在调用每一个方法之前,我们都要打开一个大文件并读入文件内容,这绝对是一个非常耗费时间的操作。如果我们使用 @Before 和 @After ,那么每次测试都要读取一次文件,效率及其低下。这里我们所希望的是在所有测试一开始读一次文件,所有测试结束之后释放文件,而不是每次测试都读文件。所以,JUnit4为我们提供了@BeforeClass和@AfterClass标注方式。

    @BeforeClass

         标明在执行某个类的所有测试方法之前仅执行一遍的方法

    @AfterClass

         标明在执行某个类的所有测试方法之后仅执行一遍的方法

注:@BeforeClass和@AfterClass标明的方法在同一个类中同样只能各有一个,且必须用public  static 关键字修饰。

我们可以通过@BeforeClass对本次测试代码进行规范。例如,将

private Counter counter = new Counter();//创建并初始化待测类对象
改为:

private static Counter counter;//创建并初始化待测类对象
	@BeforeClass
	public static void initCounter(){
		counter = new Counter();
	}

测试结果是一样的。

三、@Test(timeout=毫秒值)标注限时测试

有时我们需要对某些方法的执行效率或者是避免方法中出现死循环进行测试,这时我们可以通过JUnit4的@Test(timeout=毫秒值)方法为这些测试函数设定一个执行时间,如果超过了这个时间,测试就会强行终止,测试失败,测试结果会向你表明失败的原因是由于待测方法运行超时。

限时测试的格式:

              @Test(timeout=毫秒值)

              测试方法

       标明用来测试某个方法执行效率的测试方法,如果被测试方法在规定的时间(timeout)之内没有运行完,则测试失败。

      例:去掉本次测试方法testSquareRoot()上的@Ignore.如下图:


运行一下测试程序,结果为:


四、@Test(expected= *.class)标注异常测试

在java代码的编写中,经常会编写一些需要抛出异常的方法,那么,如果一个方法在运行的时候本该抛出异常但实际上并没有抛出,这也是一种bug。所以JUnit为我们提供了一个专门测试异常的方法,即通过@Test(expected = *.class)的方式进行异常测试。

      @Test(expected= *.class)

              标明用来测试异常的测试方法,expected属性的值是一个异常的类型。

例如:在待测类Counter中的divideCounter(int x)方法,当除数为零时会抛出ArithmeticException异常,下面我们编写代码对此异常能否正常抛出进行测试:代码如下:

@Test(expected = ArithmeticException.class)
	public void testDivideZero(){
		counter.divideCounter(0);
	}

结果如下:



五、参数化测试

在我们一开始写的被测类Counter中存在着判断一个数是否等于0的方法equalTen,这个方法的参数分为3个区域(大于10,等于10,小于10),为保证传入每个区域的参数都能得到我们期待的结果,所以我们需要对每个区域的参数进行测试。测试代码如下:

<span style="white-space:pre">	</span>@Test
	public void testEqualTen1(){
		counter.equalTen(5);
		assertEquals(-1,counter.getResult());
	}
	@Test
	public void testEqualTen2(){
		counter.equalTen(10);
		assertEquals(0,counter.getResult());
	}
	@Test
	public void testEqualTen3(){
		counter.equalTen(15);
		assertEquals(1,counter.getResult());
	}

运行结果为:


我们需要三个测试方法对3个区域的参数分别进行测试,如果参数区域很多的话,这将是一件非常麻烦的事,为了简化类似的测试,JUnit4中为我们提供了参数化测试的概念,通过参数化测试我们可以只写一个测试函数,而把这若干种情况作为参数传递进去,即可一次性完成测试。

这要求我们在测试目录下,新建一个类EqualTenTest.class

EqualTenTest类中的代码为:

package com.edward.junit;

import static org.junit.Assert.*;

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

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)//为本测试类指定一个运行器Parameterized.class
public class EqualTenTest {
	private static Counter counter;//声明待测类的引用
	private int param;//声明全局变量,存放测试参数
	private int result;//声明全局变量,存放期待结果
	
	@BeforeClass
	public static void initCounter(){
		counter = new Counter();//初始化待测类对象
	}
	
	@Before
	public void setUp() throws Exception {
		counter.resetResult();//在执行每个测试方法之前,重置计算结果result
	}
	@Parameters//标明本方法用来初始化测试类的参数集合
	public static Collection data(){//定义并初始化测试数据集合
		return Arrays.asList(new Object[][]{
				{15,1},{10,0},{5,-1}
		});
	}
	public EqualTenTest(int param,int result){//构造函数对全局变量进行初始化
		this.param = param;
		this.result = result;
	}
	@Test
	public void testEqualTen(){//完成测试方法
		counter.equalTen(param);
		assertEquals(result,counter.getResult());
	}
}

参数化测试类的完成步骤为:

第一步,在测试目录下定义一个新的类,为这个类指定一个特殊的Runner(运行器) @RunWith(Parameterized.class);

第二步,声明并初始化待测类的对象

第三步,声明两个全局变量,一个用于存放测试参数,另一个用于存放期待结果,并在构造方法中对这两个变量进行初始化。

第四步,创建一个方法定义并初始化测试数据的集合,该方法必须用@Parameter进行标注,其中的数据为 一个二维数组,数据两两一组,每组中的两个数据,一个是测试参数,一个是期待结果,分别对应赋值给声明的两个全局变量,所以,其顺序应该和构造方法中两个参数的顺序一致。

第五步,完成测试方法,测试方法中调用被测方法传参时,传入存放测试参数的全局变量,assertEquals(,);中的第一个参数,传入存放期待结果的第一个参数。

运行结果如下:

 六,@RunWith(***.class)标注执行运行测试类的运行器

JUnit框架通过Runner(运行器)来运行我们提交的测试代码,通常情况下,有默认的运行器执行,不用我们去特殊指定。但在JUnit中存在多个Runner,每个Runner都有其各自的特殊功能,我们可以根据需要选择不同的Runner来运行我们的测试代码,比如在参数化测试时,我们通过@RunWith(Parameterized.class)为参数化测试类指定了运行器:Parameterized.class

JUnit4中为我们提供的默认运行器为:BlockJunit4ClassRunner.class

我们可以在我们的测试类CounterTest中指定BlockJunit4ClassRunner.class代码为:

@RunWith(BlockJUnit4ClassRunner.class)//为测试类指定默认运行器
public class CounterTest {

运行结果与不指定是一样的。

七, @RunWith(Suite.class )标注进行打包测试

在实际应用中,测试一个项目时,我们需要很多个测试类,但是这些测试类我们再一个一个的去运行将会非常麻烦,所以,JUnit为我们提供一个可以一次性运行所以测试类的方法,那就是打包测试,打包测试需要我们在测试目录下新建一个类,类名随意,在此我们新建一个类AllProjectTest.class。

项目目录如下图:

AllProjectTest中的代码如下:

package com.edward.junit;

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

@RunWith(Suite.class)//为打包测试类指定一个特殊的运行器
@Suite.SuiteClasses({CalculatorTest.class,CounterTest.class,EqualTenTest.class})//标明本类为打包测试类,并把需要打包测试的类作为参数传递给该标注即可。
public class AllProjectTest {

}

打包测试类的完成步骤为:

第一步:在测试目录下新建一个类,类名随意,内容为空,用于执行打包测试。

第二步:通过@RunWith(Suite.class)标记,为打包测试类指定一个特殊的运行器

第三步:通过@Suite.SuiteClasses({需要打包测试的类名})//标明本类为打包测试类,并把需要打包测试的类作为参数传递给该标注即可。

运行结果为:





  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值