采用JUnit4进行单元测试.docx
《采用JUnit4进行单元测试.docx》由会员分享,可在线阅读,更多相关《采用JUnit4进行单元测试.docx(16页珍藏版)》请在冰豆网上搜索。
采用JUnit4进行单元测试
采用JUnit4进行单元测试
1、再产生一个测试项目JUnit4TestProject
2、在本测试项目中引用应用项目
3、在测试项目中添加JUnit4的系统包文件
4、对Calculator类添加对应的测试用例类TestCalculatorJUnit4
将产生出下面的模板程序
packagecom.px1987.junit4;
importstaticorg.junit.Assert.*;
importorg.junit.After;
importorg.junit.AfterClass;
importorg.junit.Before;
importorg.junit.BeforeClass;
importorg.junit.Test;
publicclassTestCalculatorJUnit4{
@BeforeClass
publicstaticvoidsetUpBeforeClass()throwsException{
}
@AfterClass
publicstaticvoidtearDownAfterClass()throwsException{
}
@Before
publicvoidsetUp()throwsException{
}
@After
publicvoidtearDown()throwsException{
}
@Test
publicvoidtestAdd(){
fail("Notyetimplemented");
}
}
5、对上面的代码进行说明
(1)与JUnit4框架有关的系统包
注意在测试类中所用到的与JUnit4框架有关的系统包,最主要的是是org.junit.*。
把它包含进来之后,绝大部分功能就有了。
还有一句话也非常地重要“importstaticorg.junit.Assert.*;”,我们在测试的时候所使用的一系列assertEquals方法就来自这个包。
所应该注意的是,这是一个静态包含(static),是JDK5中新增添的一个功能。
也就是说assertEquals是Assert类中的一系列的静态方法,一般的使用方式是Assert.assertEquals(),但是使用了静态包含后,前面的类名就可以省略了,使用起来更加的方便。
(2)各种形式的注释
由于测试类是一个独立的类,没有任何父类。
测试类的名字也可以任意命名,没有任何局限性。
所以我们不能通过类的声明来判断它是不是一个测试类,它与普通类的区别在于它内部的方法的声明,而这是通过其中的各种形式的注释来标示的。
“标注”也是JDK5的一个新特性,用在此处非常恰当。
我们可以看到,在某些方法的前有@Before、@Test、@Ignore等字样,这些就是标注,以一个“@”作为开头。
这些标注都是JUnit4自定义的,熟练掌握这些标注的含义非常重要。
6、各种形式的注释的说明
(1)@Ignore忽略测试某些尚未完成的方法。
如果我们在写程序前做了很好的规划,那么哪些方法是什么功能都应该实现定下来。
因此,即使该方法尚未完成,他的具体功能也是确定的,这也就意味着你可以为他编写测试用例。
但是,如果我们已经把该方法的测试用例写完,但该方法尚未完成,那么测试的时候一定是“失败”。
这种失败和真正的失败是有区别的,因此JUnit提供了一种方法来区别他们,那就是在这种测试函数的前面加上@Ignore标注,这个标注的含义就是“某些方法尚未完成,暂不参与此次测试”。
这样的话测试结果就会提示我们有几个测试被忽略,而不是失败。
一旦我们完成了相应函数,只需要把@Ignore标注删去,就可以进行正常的测试。
(2)@Before在任何一个测试执行之前必须执行的代码
我们非常希望每一个测试都是独立的,相互之间没有任何耦合度。
因此,我们就很有必要在执行每一个测试之前,需要进行一些初始化的操作,这样的方法可以采用@Before注释。
JUnit3测试运行程序会在运行每个测试之前自动调用setUp()方法。
该方法一般会初始化字段,打开日志记录,重置环境变量,等等。
在JUnit4中,仍然可以在每个测试方法运行之前初始化字段和配置环境。
然而,完成这些操作的方法不再需要叫setUp(),只要用@Before注释来指示即可。
注意,也可以用多个@Before来注释多个方法在测试之前运行。
(3)@After在任何测试执行之后需要进行的收尾工作
同样,可以用多个@After来注释多个方法在测试之后运行。
并保证这两种方法都使用publicvoid修饰,而且不能带有任何参数。
(4)@BeforeClass
JUnit4中引入了一个JUnit中没有的新特性:
类范围的setUp()和tearDown()方法。
JUnit4中引入了一个JUnit中没有的新特性:
类范围的setUp()和tearDown()方法。
但要区分的是,@Before方法在每个@Test运行之前都会运行一次,而@BeforeClass只在该类所有测试方法运行之前刚好运行一次。
例如,假设类中的每个测试都使用一个数据库连接、一个网络连接、一个非常大的数据结构,或者还有一些对于初始化和事情安排来说比较昂贵的其他资源。
不要在每个测试之前都重新创建它,我们可以创建它一次,并还原它一次。
(5)@AfterClass
用这两个标注来标注的方法,只在测试用例初始化时执行@BeforeClass方法,当所有测试执行完毕之后,执行@AfterClass进行收尾工作。
在这里要注意一下,每个测试类只能有一个方法被标注为@BeforeClass或@AfterClass,并保证这两种方法都使用publicstaticvoid修饰,而且不能带有任何参数。
(6)限时测试(单位为毫秒)
对于那些逻辑很复杂,循环嵌套比较深的程序,很有可能出现死循环,因此一定要采取一些预防措施。
限时测试是一个很好的解决方案。
我们给这些测试方法设定一个执行时间,超过了这个时间,他们就会被系统强行终止,并且系统还会向我们汇报该方法结束的原因是因为超时,这样我们就可以发现这些Bug了。
要实现这一功能,只需要给@Test标注加一个参数即可,代码如下:
@Test(timeout=1000)//限时测试的方法(单位为毫秒)
publicvoidtestMul(){
}
但是由于并不能准确反应实际时间,所以应用较少,因为测试误差太大绝对不适合拿来做超时测试的!
(7)测试异常
如果我们觉得一个方法应该抛出异常,但是它没有正常地抛出,这算不算Bug呢?
这当然是Bug,JUnit对此也考虑到了这一点,来帮助我们找到这种Bug。
Junit4为测试方法增加了判断异常的方式,避免了以前还要通过try/catch块捕捉异常再抛出的复杂方式。
例如,我们写的计算器类有除法功能,如果除数是一个0,那么必然要抛出“除0异常”。
因此,我们很有必要对这些进行测试。
publicvoidtestDiv(){
try{
intn=2/0;
fail("Dividedbyzero!
");
}
catch(ArithmeticExceptionsuccess){
assertNotNull(success.getMessage());
}
}
该方法不仅难看,而且试图挑战代码覆盖工具,因为不管测试是否通过还是失败,总有一些代码不被执行。
在JUni4中,可以编写抛出异常的代码,并使用注释来声明该异常是预期的:
代码如下:
@Test(expected=ArithmeticException.class)
publicvoidtestDiv(){
}
我们需要使用@Test标注的expected属性,将我们要检验的异常传递给他;Junit4就会检查此方法是否抛出ArithmeticException异常,如果抛出则测试通过,没抛出则测试不通过(如果没有异常抛出或者抛出一个不同的异常,那么测试就将失败。
)。
7、编程该TestCalculatorJUnit4类
packagecom.px1987.junit4;
importjava.util.Arrays;
importjava.util.Collection;
importorg.junit.After;
importorg.junit.AfterClass;
importorg.junit.Assert;
importorg.junit.Before;
importorg.junit.BeforeClass;
importorg.junit.Ignore;
importorg.junit.Test;
importorg.junit.runners.Parameterized.Parameters;
importcom.px1987.junit.Calculator;
publicclassTestCalculatorJUnit4{
staticCalculatoroneCalculator=null;
@BeforeClass
publicstaticvoidsetUpBeforeClass()throwsException{
oneCalculator=newCalculator();
}
@AfterClass
publicstaticvoidtearDownAfterClass()throwsException{
oneCalculator=null;
}
@Before
publicvoidsetUp()throwsException{
}
@After
publicvoidtearDown()throwsException{
}
@Test
publicvoidtestAdd(){
doubleonedigit=1.0;
doubletwodigit=2.0;
doublereturnResult=oneCalculator.add(onedigit,twodigit);
Assert.assertEquals(returnResult,3.0);
}
@Ignore//忽略测试某些尚未完成的方法。
publicvoidtestSub(){
}
@Test(timeout=1000)//限时测试的方法(单位为毫秒)
publicvoidtestMul(){
}
@Test(expected=ArithmeticException.class)//测试异常
publicvoidtestDiv(){
intx=1;
inty=0;
intz=x/y;
}
@Parameters
publicstaticCollectionmultipleValues(){
returnArrays.asList(newObject[][]{
{3,2,6},
{4,3,12},
{21,5,105},
{11,22,242},
{8,9,72}});
}
}
8、Junit4中的参数化测试
(1)应用场合
为了保证单元测试的严谨性,我们模拟了不同类型的字符串来测试方法的处理能力,为此我们编写大量的单元测试方法。
可是这些测试方法都是大同小异:
代码结构都是相同的,不同的仅仅是测试数据和期望值。
有没有更好的方法将测试方法中相同的代码结构提取出来,提高代码的重用度,减少复制粘贴代码的烦恼?
在以前的JUnit版本上,并没有好的解决方法,而现在我们可以使用JUnit提供的参数化测试方式应对这个问题。
(2)实现的过程
●为准备使用参数化测试的测试类指定特殊的运行器org.junit.runners.Parameterized。
@RunWith(Parameterized.class)
publicclassTestCalculatorJUnit4Parameter{
}
●为测试类声明几个变量,分别用于存放期望值和测试所用数据。
privateintexpectedResult;
privateintparameterOne;
privateintparameterTwo;
●为测试类声明一个使用注解org.junit.runners.Parameterized.Parameters修饰的,返回值为java.util.Collection的公共静态方法,并在此方法中初始化所有需要测试的参数对。
@Parameters
publicstaticCollectionaddValues(){
returnArrays.asList(newObject[][]{
{3,2,5},
{4,3,7},
{21,5,26},
{11,22,33},
{8,9,17}});
}
●为测试类声明一个带有参数的公共构造函数,并在其中为第二个环节中声明的几个变量赋值。
publicTestCalculatorJUnit4Parameter(intparameterOne,intparameterTwo,intexpectedResult){
this.parameterOne=parameterOne;
this.parameterTwo=parameterTwo;
this.expectedResult=expectedResult;
}
●编写测试方法,使用定义的变量作为参数进行测试。
publicvoidtestAdd(){
doublereturnResult=oneCalculator.add(parameterOne,parameterTwo);
Assert.assertEquals(expectedResult,returnResult);
}
(3)最终实现的代码
packagecom.px1987.junit4;
importjava.util.Arrays;
importjava.util.Collection;
importorg.junit.After;
importorg.junit.AfterClass;
importorg.junit.Assert;
importorg.junit.Before;
importorg.junit.BeforeClass;
importorg.junit.Test;
importorg.junit.runner.RunWith;
importorg.junit.runners.Parameterized;
importorg.junit.runners.Parameterized.Parameters;
importcom.px1987.junit.Calculator;
@RunWith(Parameterized.class)
publicclassTestCalculatorJUnit4Parameter{
staticCalculatoroneCalculator=null;
privateintexpectedResult;
privateintparameterOne;
privateintparameterTwo;
/**
*参数化测试必须的构造函数
*/
publicTestCalculatorJUnit4Parameter(intparameterOne,intparameterTwo,intexpectedResult){
this.parameterOne=parameterOne;
this.parameterTwo=parameterTwo;
this.expectedResult=expectedResult;
}
@BeforeClass
publicstaticvoidsetUpBeforeClass()throwsException{
oneCalculator=newCalculator();
}
@AfterClass
publicstaticvoidtearDownAfterClass()throwsException{
oneCalculator=null;
}
@Parameters
publicstaticCollectionaddValues(){
returnArrays.asList(newObject[][]{
{3,2,5},
{4,3,7},
{21,5,26},
{11,22,33},
{8,9,17}});
}
@Test
publicvoidtestAdd(){
doublereturnResult=oneCalculator.add(parameterOne,parameterTwo);
Assert.assertEquals(expectedResult,returnResult);
}
}
(4)执行该测试用例的结果
9、采用测试套件进行测试TestCalculatorJUnit4Suit
测试套件可以将多个测试用例合在一起测试,将相关的测试用例合成一个测试套件,在做一个修改后,只需要运行测试套件就可以,不需要运行每一个测试用例。
(1)添加一个类
(2)编程该类
packagecom.px1987.junit4;
importorg.junit.runner.RunWith;
importorg.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses(
{TestCalculatorJUnit4.class
}
)
publicclassTestCalculatorJUnit4Suit{
publicTestCalculatorJUnit4Suit(){
}
}
Junit4没有采用以前的套件测试方法,同样使用annotation的方式来进行。
只需要在所要构建测试套件的包里创建一个类文件,该类的名称可以为任意的类名。
(3)在该类中需要一个特殊的Runner
我们需要向@RunWith标注传递一个参数Suite.class。
同时,我们还需要另外一个标注@Suite.SuiteClasses(将需要组成套件运行的类放到此属性中),来表明这个类是一个测试套件类(包含无参数构造方法的类)。
我们需要将添加到测试套件中的各个类作为参数传递给该标注就可以了。
有了这两个标注之后,就已经完整的表达了所有的含义,因此实际的测试套件类本身的类名称已经无关紧要,随便取一个类名,内容全部为空既可。
(4)执行该测试套件
10、JUnit4为比较数组添加了两个assert()方法
(1)publicstaticvoidassertEquals(Object[]expected,Object[]actual)
(2)publicstaticvoidassertEquals(Stringmessage,Object[]expected,Object[]actual)
这两个方法以最直接的方式比较数组:
如果数组长度相同,且每个对应的元素相同,则两个数组相等,否则不相等。
数组为空的情况也作了考虑。