资源描述
,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,2012-11-6,#,Junit4,教程,1/33,Junit4,概述,JUnit4,是,JUnit,框架有史以来最大改进,其主要目标便是利用,Java5,Annotation,特征简化测试用例编写。,先简单解释一下什么是,Annotation,,这个单词普通是翻译成元数据。元数据是什么?元数据就是描述数据数据。也就是说,这个东西在,Java,里面能够用来和,public,、,static,等关键字一样来修饰类名、方法名、变量名。修饰作用描述这个数据是做什么用,差不多和,public,描述这个数据是公有一样。想详细了解能够看,Core Java2,。废话不多说了,直接进入正题。,2/33,我们先看一下在,JUnit 3,中我们是怎样写一个单元测试。比以下面一个类:,public class AddOperation public int add(int x,int y)return x+y;,3/33,我们要测试,add,这个方法,我们写单元测试得这么写:,import junit.framework.TestCase;import static org.junit.Assert.*;public class AddOperationTest extends TestCase public void setUp()throws Exception public void tearDown()throws Exception public void testAdd()System.out.println(add);int x=0;int y=0;AddOperation instance=new AddOperation();int expResult=0;int result=instance.add(x,y);assertEquals(expResult,result);,4/33,能够看到上面类使用了,JDK5,中静态导入,这个相对来说就很简单,只要在,import,关键字后面加上,static,关键字,就能够把后面类,static,变量和方法导入到这个类中,调用时候和调用自己方法没有任何区分。,我们能够看到上面那个单元测试有一些比较霸道地方,表现在:,1.,单元测试类必须继承自,TestCase,。,2.,要测试方法必须以,test,开头。,5/33,假如上面那个单元测试在,JUnit 4,中写就不会这么复杂。代码以下:,import junit.framework.TestCase;import org.junit.After;import org.junit.Before;import org.junit.Test;import static org.junit.Assert.*;public class AddOperationTest extends TestCase public AddOperationTest()Before public void setUp()throws Exception After public void tearDown()throws Exception Test public void add()System.out.println(add);int x=0;int y=0;AddOperation instance=new AddOperation();int expResult=0;int result=instance.add(x,y);assertEquals(expResult,result);,6/33,我们能够看到,采取,Annotation,JUnit,已经不会霸道要求你必须继承自,TestCase,了,而且测试方法也无须以,test,开头了,只要以,Test,元数据来描述即可。,从上面例子能够看到在,JUnit 4,中还引入了一些其它元数据,下面一一介绍:,Before,:,使用了该元数据方法在每个测试方法执行之前都要执行一次。,After,:,使用了该元数据方法在每个测试方法执行之后要执行一次。,注意:,Before,和,After,标示方法只能各有一个。这个相当于取代了,JUnit,以前版本中,setUp,和,tearDown,方法,当然你还能够继续叫这个名字,不过,JUnit,不会霸道要求你这么做了。,7/33,Test(expected=*.class),在,JUnit4.0,之前,对错误测试,我们只能经过,fail,来产生一个错误,并在,try,块里面,assertTrue,(,true,)来测试。现在,经过,Test,元数据中,expected,属性。,expected,属性值是一个异常类型,Test(timeout=xxx):,该元数据传入了一个时间(毫秒)给测试方法,,假如测试方法在制订时间之内没有运行完,则测试也失败。,ignore,:,该元数据标识测试方法在测试中会被忽略。当测试方法还没有实现,或者测试方法已经过时,或者在某种条件下才能测试该方法(比如需要一个数据库联接,而在当地测试时候,数据库并没有连接),那么使用该标签来标示这个方法。同时,你能够为该标签传递一个,String,参数,来表明为何会忽略这个测试方法。比如:,lgnore(“,该方法还没有实现,”),,在执行时候,仅会汇报该方法没有实现,而不会运行测试方法。,8/33,在,Eclipse,中使用,JUnit4,进行单元测试,(初級),我们在编写大型程序时候,需要写成千上万个方法或函数,这些函数功效可能很强大,但我们在程序中只用到该函数一小部分功效,而且经过调试能够确定,这一小部分功效是正确。不过,我们同时应该确保每一个函数都完全正确,因为假如我们今后假如对程序进行扩展,用到了某个函数其它功效,而这个功效有,bug,话,那绝对是一件非常郁闷事情。所以说,每编写完一个函数之后,都应该对这个函数方方面面进行测试,这么测试我们称之为单元测试。传统编程方式,进行单元测试是一件很麻烦事情,你要重新写另外一个程序,在该程序中调用你需要测试方法,而且仔细观察运行结果,看看是否有错。正因为如此麻烦,所以程序员们编写单元测试热情不是很高。于是有一个牛人推出了单元测试包,大大简化了进行单元测试所要做工作,这就是,JUnit4,。本文简明介绍一下在,Eclipse3.2,中使用,JUnit4,进行单元测试方法。,首先,我们来一个傻瓜式速成教程,不要问为何,,Follow Me,,先来体验一下单元测试快感!,9/33,首先新建一个项目叫,JUnit_Test,,我们编写一个,Calculator,类,这是一个能够简单实现加减乘除、平方、开方计算器类,然后对这些功效进行单元测试。这个类并不是很完美,我们有意保留了一些,Bug,用于演示,这些,Bug,在注释中都有说明。该类代码以下:,public class Calculator.,private static int result;/,静态变量,用于存放运行结果,public void add(int n).,result=result+n;,public void substract(int n).,result=result-1;/Bug:,正确应该是,result=result-n,public void multiply(int n)./,此方法还未写好,public void divide(int n).,result=result/n;,public void square(int n).result=n*n;,public void squareRoot(int n).,for(;);/Bug:,死循环,public void clear()./,将结果清零,result=0;,public int getResult().,return result;,10/33,第二步,将,JUnit4,单元测试包引入这个项目:在该项目上点右键,点,“,属性,”,,如图:,11/33,在弹出属性窗口中,首先在左边选择,“Java Build Path”,,然后到右上选择,“Libraries”,标签,之后在最右边点击,“Add Library”,按钮,以下列图所表示:,12/33,然后在新弹出对话框中选择,JUnit4,并点击确定,如上图所表示,,JUnit4,软件包就被包含进我们这个项目了。,第三步,生成,JUnit,测试框架:在,Eclipse,Package Explorer,中用右键点击该类弹出菜单,选择,“New JUnit Test Case”,。以下列图所表示:,13/33,在弹出对话框中,进行对应选择,以下列图所表示:,14/33,点击,“,下一步,”,后,系统会自动列出你这个类中包含方法,选择你要进行测试方法。此例中,我们仅对,“,加、减、乘、除,”,四个方法进行测试。以下列图所表示:,15/33,之后系统会自动生成一个新类,CalculatorTest,,里面包含一些空测试用例。你只需要将这些测试用例稍作修改即可使用。完整,CalculatorTest,代码以下:,import static org.junit.Assert.*;,import org.junit.Before;,import org.junit.Ignore;,import org.junit.Test;,public class CalculatorTest.,private static Calculator calculator=new Calculator();,Before,public void setUp()throws Exception.,calculator.clear();,Test,public void testAdd().,calculator.add(2);,calculator.add(3);,assertEquals(5,calculator.getResult();,16/33,Test,public void testSubstract().,calculator.add(10);,calculator.substract(2);,assertEquals(8,calculator.getResult();,Ignore(Multiply()Not yet implemented),Test,public void testMultiply().,Test,public void testDivide().,calculator.add(8);,calculator.divide(2);,assertEquals(4,calculator.getResult();,17/33,第四步,运行测试代码:按照上述代码修改完成后,我们在,CalculatorTest,类上点右键,选择,“Run As JUnit Test”,来运行我们测试,以下列图所表示:,18/33,运行结果如右:,进度条是红颜色表示发觉错误,详细测试结果在进度条上面有表示,“,共进行了,4,个测试,其中,1,个测试被忽略,一个测试失败,”,至此,我们已经完整体验了在,Eclipse,中使用,JUnit,方法。在接下来文章中,我会详细解释测试代码中每一个细节!,19/33,在,Eclipse,中使用,JUnit4,进行单元测试(中級),我们继续对初级篇中例子进行分析。初级篇中我们使用,Eclipse,自动生成了一个测试框架,在这篇文章中,我们来仔细分析一下这个测试框架中每一个细节,知其然更要知其所以然,才能愈加熟练地应用,JUnit4,。,一、包含必要地,Package,在测试类中用到了,JUnit4,框架,自然要把对应地,Package,包含进来。最主要地一个,Package,就是,org.junit.*,。把它包含进来之后,绝大部分功效就有了。还有一句话也非常地主要,“import static org.junit.Assert.*;”,,我们在测试时候使用一系列,assertEquals,方法就来自这个包。大家注意一下,这是一个静态包含(,static,),是,JDK5,中新增添一个功效。也就是说,,assertEquals,是,Assert,类中一系列静态方法,普通使用方式是,Assert.assertEquals(),,不过使用了静态包含后,前面类名就能够省略了,使用起来愈加方便。,二、测试类申明,大家注意到,我们测试类是一个独立类,没有任何父类。测试类名字也能够任意命名,没有任何不足。所以我们不能经过类申明来判断它是不是一个测试类,它与普通类区分在于它内部方法申明,我们接着会讲到。,20/33,三、创建一个待测试对象。,你要测试哪个类,那么你首先就要创建一个该类对象。正如上一篇文章中代码:,private static Calculator calculator=new Calculator();,为了测试,Calculator,类,我们必须创建一个,calculator,对象。,四、测试方法申明,在测试类中,并不是每一个方法都是用于测试,你必须使用,“,标注,”,来明确表明哪些是测试方法。,“,标注,”,也是,JDK5,一个新特征,用在此处非常恰当。我们能够看到,在一些方法前有,Before,、,Test,、,Ignore,等字样,这些就是标注,以一个,“”,作为开头。这些标注都是,JUnit4,自定义,熟练掌握这些标注含义非常主要。,21/33,五、编写一个简单测试方法。,首先,你要在方法前面使用,Test,标注,以表明这是一个测试方法。对于方法申明也有以下要求:名字能够随便取,没有任何限制,不过返回值必须为,void,,而且不能有任何参数。假如违反这些要求,会在运行时抛出一个异常。至于方法内该写些什么,那就要看你需要测试些什么了。比如:,Test,public void testAdd().,calculator.add(2);,calculator.add(3);,assertEquals(5,calculator.getResult();,我们想测试一下,“,加法,”,功效时候正确,就在测试方法中调用几次,add,函数,初始值为,0,,先加,2,,再加,3,,我们期待结果应该是,5,。假如最终实际结果也是,5,,则说明,add,方法是正确,反之说明它是错。,assertEquals(5,calculator.getResult();,就是来判断期待结果和实际结果是否相等,第一个参数填写期待结果,第二个参数填写实际结果,也就是经过计算得到结果。这么写好之后,,JUnit,会自动进行测试并把测试结果反馈给用户。,22/33,六、忽略测试一些还未完成方法。,假如你在写程序前做了很好规划,那么哪些方法是什么功效都应该实现定下来。所以,即使该方法还未完成,他详细功效也是确定,这也就意味着你能够为他编写测试用例。不过,假如你已经把该方法测试用例写完,但该方法还未完成,那么测试时候一定是,“,失败,”,。这种失败和真正失败是有区分,所以,JUnit,提供了一个方法来区分他们,那就是在这种测试函数前面加上,Ignore,标注,这个标注含义就是,“,一些方法还未完成,暂不参加此次测试,”,。这么话测试结果就会提醒你有几个测试被忽略,而不是失败。一旦你完成了对应函数,只需要把,Ignore,标注删去,就能够进行正常测试。,23/33,七、,Fixture,(暂且翻译为,“,固定代码段,”,),Fixture,含义就是,“,在一些阶段必定被调用代码,”,。比如我们上面测试,因为只申明了一个,Calculator,对象,他初始值是,0,,不过测试完加法操作后,他值就不是,0,了;接下来测试减法操作,就必定要考虑上次加法操作结果。这绝对是一个很糟糕设计!我们非常希望每一个测试都是独立,相互之间没有任何耦合度。所以,我们就很有必要在执行每一个测试之前,对,Calculator,对象进行一个,“,复原,”,操作,以消除其它测试造成影响。所以,,“,在任何一个测试执行之前必须执行代码,”,就是一个,Fixture,,我们用,Before,来标注它,如前面例子所表示:,Before,public void setUp()throws Exception.,calculator.clear();,这里不在需要,Test,标注,因为这不是一个,test,,而是一个,Fixture,。同理,假如,“,在任何测试执行之后需要进行收尾工作,”,也是一个,Fixture,,使用,After,来标注。因为本例比较简单,没有用到此功效。,24/33,在,Eclipse,中使用,JUnit4,进行单元测试(高級),一、高级,Fixture,上一篇文章中我们介绍了两个,Fixture,标注,分别是,Before,和,After,,我们来看看他们是否适合完成以下功效:有一个类是负责对大文件(超出,500,兆)进行读写,他每一个方法都是对文件进行操作。换句话说,在调用每一个方法之前,我们都要打开一个大文件并读入文件内容,这绝对是一个非常花费时间操作。假如我们使用,Before,和,After,,那么每次测试都要读取一次文件,效率及其低下。这里我们所希望是在全部测试一开始读一次文件,全部测试结束之后释放文件,而不是每次测试都读文件。,JUnit,作者显然也考虑到了这个问题,它给出了,BeforeClass,和,AfterClass,两个,Fixture,来帮我们实现这个功效。从名字上就能够看出,用这两个,Fixture,标注函数,只在测试用例初始化时执行,BeforeClass,方法,当全部测试执行完成之后,执行,AfterClass,进行收尾工作。在这里要注意一下,每个测试类只能有一个方法被标注为,BeforeClass,或,AfterClass,,而且该方法必须是,Public,和,Static,。,25/33,二、限时测试。,还记得我在初级篇中给出例子吗,那个求平方根函数有,Bug,,是个死循环:,public void squareRoot(int n).,for(;);/Bug:,死循环,假如测试时候碰到死循环,你脸上绝对不会露出笑容。所以,对于那些逻辑很复杂,循环嵌套比较深程序,很有可能出现死循环,所以一定要采取一些预防办法。限时测试是一个很好处理方案。我们给这些测试函数设定一个执行时间,超出了这个时间,他们就会被系统强行终止,而且系统还会向你汇报该函数结束原因是因为超时,这么你就能够发觉这些,Bug,了。要实现这一功效,只需要给,Test,标注加一个参数即可,代码以下:,Test(timeout=1000),public void squareRoot().,calculator.squareRoot(4);,assertEquals(2,calculator.getResult();,Timeout,参数表明了你要设定时间,单位为毫秒,所以,1000,就代表,1,秒。,26/33,三、测试异常,JAVA,中异常处理也是一个重点,所以你经常会编写一些需要抛出异常函数。那么,假如你以为一个函数应该抛出异常,不过它没抛出,这算不算,Bug,呢?这当然是,Bug,,并,JUnit,也考虑到了这一点,来帮助我们找到这种,Bug,。比如,我们写计算器类有除法功效,假如除数是一个,0,,那么必定要抛出,“,除,0,异常,”,。所以,我们很有必要对这些进行测试。代码以下:,Test(expected=ArithmeticException.class),public void divideByZero().,calculator.divide(0);,如上述代码所表示,我们需要使用,Test,标注,expected,属性,将我们要检验异常传递给他,这么,JUnit,框架就能自动帮我们检测是否抛出了我们指定异常。,27/33,四、,Runner(,运行器,),大家有没有想过这个问题,当你把测试代码提交给,JUnit,框架后,框架怎样来运行你代码呢?答案就是,Runner,。在,JUnit,中有很多个,Runner,,他们负责调用你测试代码,每一个,Runner,都有各自特殊功效,你要依据需要选择不一样,Runner,来运行你测试代码。可能你会以为奇怪,前面我们写了那么多测试,并没有明确指定一个,Runner,啊?这是因为,JUnit,中有一个默认,Runner,,假如你没有指定,那么系统自动使用默认,Runner,来运行你代码。换句话说,下面两段代码含义是完全一样:,import org.junit.internal.runners.TestClassRunner;,import org.junit.runner.RunWith;,/,使用了系统默认,TestClassRunner,,与下面代码完全一样,public class CalculatorTest.,RunWith(TestClassRunner.class),public class CalculatorTest.,从上述例子能够看出,要想指定一个,Runner,,需要使用,RunWith,标注,而且把你所指定,Runner,作为参数传递给它。另外一个要注意是,,RunWith,是用来修饰类,而不是用来修饰函数。只要对一个类指定了,Runner,,那么这个类中全部函数都被这个,Runner,来调用。最终,不要忘了包含对应,Package,哦,上面例子对这一点写很清楚了。接下来,我会向你们展示其它,Runner,特有功效。,28/33,五、参数化测试。,你可能碰到过这么函数,它参数有许多特殊值,或者说他参数分为很多个区域。比如,一个对考试分数进行评价函数,返回值分别为,“,优异,良好,普通,及格,不及格,”,,所以你在编写测试时候,最少要写,5,个测试,把这,5,中情况都包含了,这确实是一件很麻烦事情。我们还使用我们先前例子,测试一下,“,计算一个数平方,”,这个函数,暂且分三类:正数、,0,、负数。测试代码以下:,import org.junit.AfterClass;,import org.junit.Before;,import org.junit.BeforeClass;,import org.junit.Test;,import static org.junit.Assert.*;,public class AdvancedTest.,private static Calculator calculator,new Calculator();,Before,public void clearCalculator().,calculator.clear();,Test,public void square1().,calculator.square(2);,assertEquals(4,calculator.getResult();,29/33,Test,public void square2().,calculator.square(0);,assertEquals(0,calculator.getResult();,Test,public void square3().,calculator.square(-3);,assertEquals(9,calculator.getResult();,为了简化类似测试,,JUnit4,提出了,“,参数化测试,”,概念,只写一个测试函数,把这若干种情况作为参数传递进去,一次性完成测试。代码以下:,import static org.junit.Assert.assertEquals;,import org.junit.Test;,import org.junit.runner.RunWith;,import org.junit.runners.Parameterized;,import org.junit.runners.Parameterized.Parameters;,import java.util.Arrays;,import java.util.Collection;,30/33,RunWith(Parameterized.class),public class SquareTest.,private static Calculator calculator=new Calculator();,private int param;,private int result;,Parameters,public static Collection data().,return Arrays.asList(new Object.,.2,4,.0,0,.,3,9,);,/,结构函数,对变量进行初始化,public SquareTest(int param,int result).,this.param=param;this.result=result;,Test,public void square().,calculator.square(param);,assertEquals(result,calculator.getResult();,31/33,下面我们对上述代码进行分析。首先,你要为这种测试专门生成一个新类,而不能与其它测试共用同一个类,此例中我们定义了一个,SquareTest,类。然后,你要为这个类指定一个,Runner,,而不能使用默认,Runner,了,因为特殊功效要用特殊,Runner,嘛。,RunWith(Parameterized.class),这条语句就是为这个类指定了一个,ParameterizedRunner,。第二步,定义一个待测试类,而且定义两个变量,一个用于存放参数,一个用于存放期待结果。接下来,定义测试数据集合,也就是上述,data(),方法,该方法能够任意命名,不过必须使用,Parameters,标注进行修饰。这个方法框架就不予解释了,大家只需要注意其中数据,是一个二维数组,数据两两一组,每组中这两个数据,一个是参数,一个是你预期结果。比如我们第一组,2,4,,,2,就是参数,,4,就是预期结果。这两个数据次序无所谓,谁前谁后都能够。之后是结构函数,其功效就是对先前定义两个参数进行初始化。在这里你可要注意一下参数次序了,要和上面数据集合次序保持一致。假如前面次序是,参数,期待结果,,那么你结构函数次序也要是,“,结构函数,(,参数,期待结果,)”,,反之亦然。最终就是写一个简单测试例了,和前面介绍过写法完全一样,在此就不多说。,32/33,六、打包测试。,经过前面介绍我们能够感觉到,在一个项目中,只写一个测试类是不可能,我们会写出很多很多个测试类。可是这些测试类必须一个一个执行,也是比较麻烦事情。鉴于此,,JUnit,为我们提供了打包测试功效,将全部需要运行测试类集中起来,一次性运行完成,大大方便了我们测试工作。详细代码以下:,import org.junit.runner.RunWith;,import org.junit.runners.Suite;,RunWith(Suite.class),Suite.SuiteClasses(.,CalculatorTest.class,SquareTest.class,),public class AllCalculatorTests.,大家能够看到,这个功效也需要使用一个特殊,Runner,,所以我们需要向,RunWith,标注传递一个参数,Suite.class,。同时,我们还需要另外一个标注,Suite.SuiteClasses,,来表明这个类是一个打包测试类。我们把需要打包类作为参数传递给该标注就能够了。有了这两个标注之后,就已经完整表示了全部含义,所以下面类已经无关紧要,随便起一个类名,内容全部为空既可。,33/33,
展开阅读全文