junit3与junit4的区别.docx
- 文档编号:10664202
- 上传时间:2023-02-22
- 格式:DOCX
- 页数:12
- 大小:22.14KB
junit3与junit4的区别.docx
《junit3与junit4的区别.docx》由会员分享,可在线阅读,更多相关《junit3与junit4的区别.docx(12页珍藏版)》请在冰豆网上搜索。
junit3与junit4的区别
JUnit4的新特征
一、引言
在本文开始,我将假定,你已经了解由KentBeck和ErichGamma发明的这个Java单元测试框架并因此而略过必要的简介。
所以,我将集中分析从JUnit3.8到最新版本JUnit4的迁移过程以及其在IDE和Ant中的集成。
JUnit4是一种与其之前的版本完全不同的API,它根据Java5.0中的新特征(注解,静态导入等)构建而成。
如你所见,JUnit4更简单、更丰富和更易于使用,而且它引入了更为灵活的初始化和清理工作,还有限时的和参数化测试用例。
代码实例最能说明问题。
因此,在本文中,我将使用一个例子来展示不同的测试用例:
一个计算器。
该示例计算器很简单,效率并不高,甚至还有一些错误;它仅仅操作整数,并且把结果存储在一个静态变量中。
Substract方法并不返回一个有效的结果,而且也没有实现乘法运算,而且看上去在squareRoot方法中还存在一个错误:
无限循环。
这些错误将帮助说明使用JUnit4进行测试的有效性。
你可以打开和关闭这个计算器,而且你可以清除这些结果。
下面是其实现代码:
packagecalc;
publicclassCalculator{
privatestaticintresult;//存储结果的静态变量
publicvoidadd(intn){
result=result+n;
}
publicvoidsubstract(intn){
result=result-1;//错误:
应该是"result=result-n"
}
publicvoidmultiply(intn){}//还没实现
publicvoiddivide(intn){
result=result/n;
}
publicvoidsquare(intn){
result=n*n;
}
publicvoidsquareRoot(intn){
for(;;);//错误:
无限循环
}
publicvoidclear(){//清除结果
result=0;
}
publicvoidswitchOn(){//打开屏幕,显示"hello",并报警
result=0;//实现其它的计算器功能
}
publicvoidswitchOff(){}//显示"byebye",报警,并关闭屏幕
publicintgetResult(){
returnresult;
}
}
二、迁移一个测试类
现在,我将把一个已经使用JUnit3.8编写成的简单的测试类迁移到JUnit4。
这个类有一些缺陷:
它没有测试所有的业务方法,而且看上去在testDivide方法中还存在一个错误(8/2不等于5)。
因为还没有实现乘法运算功能,所以对其测试将被忽略。
下面,我们把两个版本的框架之间的差别以粗体显示出现于表格1中。
表格1.分别以JUnit3.8和JUnit4实现的CaculatorTest。
JUnit3.8
packagejunit3;
importcalc.Calculator;
importjunit.Framework.TestCase;
publicclassCalculatorTestextendsTestCase{
privatestaticCalculatorcalculator=newCalculator();
@OverrideprotectedvoidsetUp(){calculator.clear();}
publicvoidtestAdd(){
calculator.add
(1);
calculator.add
(1);
assertEquals(calculator.getResult(),2);
}
publicvoidtestSubtract(){
calculator.add(10);
calculator.subtract
(2);
assertEquals(calculator.getResult(),8);
}
publicvoidtestDivide(){
calculator.add(8);
calculator.divide
(2);
assertcalculator.getResult()==5;
}
publicvoidtestDivideByZero(){
try{
calculator.divide(0);
fail();
}
catch(ArithmeticExceptione){}
}
publicvoidnotReadyYetTestMultiply(){
calculator.add(10);
calculator.multiply(10);
assertEquals(calculator.getResult(),100);
}
}
JUnit4
packageJUnit4;
importcalc.Calculator;
importorg.junit.Before;
importorg.junit.Ignore;
importorg.junit.Test;
importstaticorg.junit.Assert.*;
publicclassCalculatorTest{
privatestaticCalculatorcalculator=newCalculator();
@BeforepublicvoidclearCalculator(){
calculator.clear();
}
@Testpublicvoidadd(){
calculator.add
(1);
calculator.add
(1);
assertEquals(calculator.getResult(),2);
}
@Testpublicvoidsubtract(){
calculator.add(10);
calculator.subtract
(2);
assertEquals(calculator.getResult(),8);
}
@Testpublicvoiddivide(){
calculator.add(8);
calculator.divide
(2);
assertcalculator.getResult()==5;
}
@Test(expected=ArithmeticException.class)
publicvoiddivideByZero(){
calculator.divide(0);
}
@Ignore("notreadyyet")
@Test
publicvoidmultiply(){
calculator.add(10);
calculator.multiply(10);
assertEquals(calculator.getResult(),100);
}
}
#p#
三、包
首先,你可以看到,JUnit4使用org.junit.*包而JUnit3.8使用的是junit.Framework.*。
当然,为了向后兼容性起见,JUnit4jar文件发行中加入了这两种包。
四、继承
在中,测试类不必再扩展junit.framework.TestCase;事实上,它们不必须扩展任何内容。
但是,JUnit4中使用的是注解。
为了以一个测试用例方式执行,一个JUnit4类中至少需要一个@Test注解。
例如,如果你仅使用@Before和@After注解而没有至少提供一个@Test方法来编写一个类,那么,当你试图执行它时将得到一个错误:
Java.lang.Exception:
Norunnablemethods.
五、断言(Assert)方法
因为在JUnit4中一个测试类并不继承自TestCase(在JUnit3.8中,这个类中定义了assertEquals()方法),所以你必须使用前缀语法(举例来说,Assert.assertEquals())或者(由于JDK5.0)静态地导入Assert类。
这样以来,你就可以完全象以前一样使用assertEquals方法(举例来说,assertEquals())。
另外,在JUnit4中,还引入了两个新的断言方法,它们专门用于数组对象的比较。
如果两个数组包含的元素都相等,那么这两个数组就是相等的。
publicstaticvoidassertEquals(Stringmessage,Object[]expecteds,Object[]actuals);
publicstaticvoidassertEquals(Object[]expecteds,Object[]actuals);
由于JDK5.0的自动装箱机制的出现,原先的12个assertEquals方法全部去掉了。
例如,原先JUnit3.8中的assertEquals(long,long)方法在JUnit4中要使用assertEquals(Object,Object)。
对于assertEquals(byte,byte)、assertEquals(int,int)等也是这样。
这种改进将有助于避免反模式。
在JUnit4中,新集成了一个assert关键字(见我们的例子中的divide()方法)。
你可以象使用assertEquals方法一样来使用它,因为它们都抛出相同的异常(java.lang.AssertionError)。
JUnit3.8的assertEquals将抛出一个junit.framework.AssertionFailedError。
注意,当使用assert时,你必须指定Java的"-ea"参数;否则,断言将被忽略。
六、预设环境(Fixture)
Fixture是在测试期间初始化和释放任何普通对象的方法。
在JUnit3.8中,你要使用setUp()来实现运行每一个测试前的初始化工作,然后使用tearDown()来进行每个测试后的清理。
这两个方法在TestCase类中都得到重载,因此都被唯一定义。
注意,我在这个Setup方法使用的是Java5.0内置的@Override注解-这个注解指示该方法声明要重载在超类中的方法声明。
在JUnit4中,则代之使用的是@Before和@After注解;而且,可以以任何命名(在我们的例子中是clearCalculator())来调用这些方法。
在本文后面,我将更多地解释这些注解。
七、测试
JUnit3.8通过分析它的签名来识别一个测试方法:
方法名必须以“test”为前缀,它必须返回void,而且它必须没有任何参数(举例来说,publicvoidtestDivide())。
一个不遵循这个命名约定的测试方法将被框架简单地忽略,而且不抛出任何异常(指示发生了一个错误)。
JUnit4不使用与JUnit3.8相同的约定。
一个测试方法不必以'test'为前缀,但是要使用@Test注解。
但是,正如在前一个框架中一样,一个测试方法也必须返回void并且是无参数的。
在JUnit4中,可以在运行时刻控制这个要求,并且不符合要求的话会抛出一个异常:
java.lang.Exception:
Methodxxxshouldhavenoparameters
java.lang.Exception:
Methodxxxshouldbevoid
@Test注解支持可选参数。
它声明一个测试方法应该抛出一个异常。
如果它不抛出或者如果它抛出一个与事先声明的不同的异常,那么该测试失败。
在我们的例子中,一个整数被零除应该引发一个ArithmeticException异常。
八、忽略一个测试
记住,不能执行多个方法。
然而,如果你不想让测试失败的话,你可以仅仅忽略它。
那么,在JUnit3.8中,我们是如何实现临时禁止一个测试的呢?
方法是:
通过注释掉它或者改变命名约定,这样测试运行机就无法找到它。
在我的例子中,我使用了方法名notReadyYetTestMultiply()。
它没有以“test”开头,所以它不会被识别出来。
现在的问题是,在成百上千的测试中间,你可能记不住重命名这个方法。
#p#
在JUnit4中,为了忽略一个测试,你可以注释掉一个方法或者删除@Test注解(你不能再改变命名约定,否则将抛出一个异常)。
然而,该问题将保留:
该运行机将不报告这样一个测试。
现在,你可以把@Ignore注解添加到@Test的前面或者后面。
测试运行机将报告被忽略的测试的个数,以及运行的测试的数目和运行失败的测试数目。
注意,@Ignore使用一个可选参数(一个String),如果你想记录为什么一个测试被忽略的话。
九、运行测试
在JUnit3.8中,你可以选择使用若干运行机:
文本型,AWT或者Swing。
JUnit4仅仅使用文本测试运行机。
注意,JUnit4不会显示任何绿色条来通知你测试成功了。
如果你想看到任何类型的绿色的话,那么你可能需要使用JUnit扩展或一种集成了JUnit的IDE(例如IDEA或者Eclipse)。
首先,我想使用老式但好用的junit.textui.TestRunner来运行该JUnit3.8测试类(考虑到使用assert关键字,我使用了-ea参数)。
java-eajunit.textui.TestRunnerjunit3.CalculatorTest
..F.E.
Therewas1error:
1)testDivide(junit3.CalculatorTest)java.lang.AssertionError
atjunit3.CalculatorTest.testDivide(CalculatorTest.java:
33)
Therewas1failure:
1)testSubtract(junit3.CalculatorTest)junit.framework.AssertionFailedError:
expected:
<9>
butwas:
<8>
atjunit3.CalculatorTest.testSubtract(CalculatorTest.java:
27)
FAILURES!
!
!
Testsrun:
4,Failures:
1,Errors:
1
TestDivide产生一个错误,因为断言确定了8/2不等于5。
TestSubstract产生一个失败,因为10-2应该等于8,但是在这个实现中存在一个错误:
它返回9。
现在,我使用新的org.junit.runner.JUnitCore运行机来运行这两个类。
注意,它能执行JUnit4和JUnit3.8测试,甚至是这二者的结合。
java-eaorg.junit.runner.JUnitCorejunit3.CalculatorTest
JUnitversion4.1
..E.E.
Therewere2failures:
1)testSubtract(junit3.CalculatorTest)
junit.framework.AssertionFailedError:
expected:
<9>butwas:
<8>
atjunit.framework.Assert.fail(Assert.java:
47)
2)testDivide(junit3.CalculatorTest)
java.lang.AssertionError
atjunit3.CalculatorTest.testDivide(CalculatorTest.java:
33)
FAILURES!
!
!
Testsrun:
4,Failures:
2
***
java-eaorg.junit.runner.JUnitCoreJUnit4.CalculatorTest
JUnitversion4.1
...E.EI
Therewere2failures:
1)subtract(JUnit4.CalculatorTest)
java.lang.AssertionError:
expected:
<9>butwas:
<8>
atorg.junit.Assert.fail(Assert.java:
69)
2)divide(JUnit4.CalculatorTest)
java.lang.AssertionError
atJUnit4.CalculatorTest.divide(CalculatorTest.java:
40)
FAILURES!
!
!
Testsrun:
4,Failures:
2
第一个非常明显的区别是,JUnit版本号被显示于控制台中(4.1)。
第二个区别是,JUnit3.8区分失败和错误;JUnit4则仅使用失败进行简化。
一个新奇的地方是,字母“I”,它显示一个测试被忽略。
十、高级测试
现在,我将展示JUnit4的一些高级特征。
列表1(见下载源码)是一个新的测试类-AdvancedTest,它派生自AbstractParent。
(一)高级预设环境
两个类都使用新的注解@BeforeClass和@AfterClass,还有@Before和@After。
表格2展示了在这些注解之间的主要区别。
表格2.@BeforeClass/@AfterClass比较于@Before/@After。
@BeforeClass和@AfterClass
@Before和@After
在每个类中只有一个方法能被注解。
多个方法能被注解,但其执行的顺序未特别指定,且不运行重载方法。
方法名是不相关的。
方法名是不相关的。
每个类运行一次。
在每个测试方法运行前或运行后运行。
在当前类的@BeforeClass方法运行前先运行超类的@BeforeClass方法。
在超类中声明的@AfterClass方法将在所有当前类的该方法运行后才运行。
超类中的@Before在所有子类的该方法运行前运行。
在超类中的@After在在所有子类的该方法运行后才运行。
必须是公共和非静态的。
必须是公共和非静态的。
即使一个@BeforeClass方法抛出一个异常,所有的@AfterClass方法也保证被运行。
即使一个@Before或者@Test方法抛出一个异常,所有的@After方法也保证被运行。
如果你仅有一次需要分配和释放昂贵的资源,那么@BeforeClass和@AfterClass可能很有用。
在我们的例子中,AbstractParent使用这些在startTestSystem()和stopTestSystem()方法上的注解启动和停止整个测试系统。
并且它使用@Before和@After初始化和清除系统。
子类AdvancedTest也混合使用这些注解。
在你的测试代码中使用System.out.println不是一种良好的实践习惯;但是,在这个用例中,它有助于理解这些注解被调用的顺序。
当我运行AdvancedTest时,我得到如下结果:
Starttestsystem//父类的@BeforeClass
Switchoncalculator//子类的@BeforeClass
Initializetestsystem//第一个测试
Clearcalculator
Initializetestsystem//第二个测试
Clearcalculator
Cleantestsystem
Initializetestsystem//第三个测试
Clearcalculator
Cleantestsystem
Initializetestsystem//第四个测试
Clearcalculator
Cleantestsystem
Switchoffcalculator//子类的@AfterClass
Stoptestsystem//父类的@AfterClass
如你所见,@BeforeClass和@AfterClass仅被调用一次,而@Before和@Afterare在每次测试中都要调用。
(二)限时测试
在前面的例子中,我为squareRoot()方法编写了一个测试用例。
记住,在这个方法中存在一个错误-能够导致它无限循环。
如果没有结果的话,我想让这个测试在1秒钟后退出。
这一功能正是timeout参数所要实现的。
@Test注解的第二个可选参数(第一个参数是必需的)可以使一个测试失败,如果该测试花费比一个预先确定的时限(毫秒)还长的时间的话。
当我运行该测试时,我得到如下的运行结果:
Therewas1failure:
1)squareRoot(JUnit4.AdvancedTest)
Java.lang.Exception:
testtimedoutafter1000milliseconds
atorg.junit.internal.runners.TestMethodRunner.runWithTimeout(TestMethodRunner.java:
68)
atorg.junit.internal.runners.TestMethodRunner.运行(TestMethodRunner.java:
43)
FAILURES!
!
!
Testsrun:
4,Failures:
1
(三)参数化测试
在列表1中,我测试了squareRoot(它是square方法而不是squareRoot方法)-通过创建若干测试方法(square2,square4,square5),这些方法都完成相同的事情(通过被一些变量参数化实现)。
其实,现在这里的复制/粘贴技术可以通过使用一个参数化测试用例加以优化(列表2)。
在列表2(见本文相应下载源码)中的测试用例使用了两个新的注解。
当一个类被使用@RunWith注释时,JUnit将调用被参考的类来运行该测试而不是使用缺省的运行机。
为了使用一个参数化测试用例,你需要使用运行机org.junit.runners.Parameterized。
为了确定使用哪个参数,该测试用例需要一个公共静态方法(在此是data(),但是名字似乎无关),该方法返回一个Collection,并且被使用@参数加以注解。
你还需要一个使用这些参数的公共构造函数。
当运行这个类,该输出是:
javaorg.junit.runner.JUnitCoreJUnit4.SquareTest
JUnitversion4.1
.......E
Therewas1failure:
1)square[6](JUnit4.Squa
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- junit3 junit4 区别