JUnit单元测试基础基础实验文档格式.docx
《JUnit单元测试基础基础实验文档格式.docx》由会员分享,可在线阅读,更多相关《JUnit单元测试基础基础实验文档格式.docx(16页珍藏版)》请在冰豆网上搜索。
使用JUnit设计测试脚本
在Calculator类中添加subtract(),multiply(),divide()后,如何编写手工测试用例和基于JUnit框架的测试用例应该如何编写?
(2)使用默认的TestSuite,显式调用JunitTestRunner
图4:
显式调用JunitTestRunner
/*
*调用由TestRunner自动创建的TestSuite对象
*默认的TestSuite对象将扫描测试类,找出所有以test开头的方法,
*为每一个testXXX方法都创建一个TestCase实例。
*要调用的方法的名称会传给TestCase的构造函数,
*这样每个实例就拥有了一个独一无二的标示。
*/
publicstaticTestSuitesuite(){
returnnewTestSuite(TestCalculatorWithJunit.class);
}
publicstaticvoidmain(String[]args){
//junit.textui.TestRunner.run(TestCalculatorWithJunit.class);
junit.swingui.TestRunner.run(TestCalculatorWithJunit.class);
(3)创建TestAll
缺省的TestSuite设计目的在于让测试人员可以轻松应对简单的测试情形。
但是当要组合多个Suite,把它作为主Suite的一部分,特别是这些suite来自不同的包;
或者要运行多个Suite、在一个Suite中选一些测试来执行……等情况下,这就需要创建自己的suite对象。
JunitFramework中,Test接口如下:
publicinterfaceTest{
/**
*Countsthenumberoftestcasesthatwillberunbythistest.
*/
publicabstractintcountTestCases();
*RunsatestandcollectsitsresultinaTestResultinstance.
publicabstractvoidrun(TestResultresult);
而TestSuite的addTest():
publicvoidaddTest(Testtest){
fTests.addElement(test);
即可以为TestSuite添加TestSuite也可以添加TestCase,这就为创建特殊的suite或者组合出TestAll创造了方便。
通常情况下,TestAll仅仅包括一个静态的suite(),该方法会注册应用程序需要定期执行的所有Test对象(包括TestCase和TestSuite)。
TestAll的suite()方法细节如下:
publicstaticTestsuite(){
TestSuitesuite=newTestSuite("
Alltestswillbeexecuted"
);
suite.addTestSuite(TestCalculatorWithJunit.class);
returnsuite;
}
JUnit单元测试的步骤
1.Junit三重唱
当你需要编写更多的TestCase的时候,你可以创建更多的TestCase对象。
当你需要一次执行多个TestCase对象的时候,您可以创建一个TestSuite对象或使用缺省的TestSuite对象进行封装。
为了执行TestSuite,需要使用TestRunner。
通过TestRunner的执行生成TestResult对象。
(如图5所示)
图5:
JUnit成员三重唱,共同产生测试结果
(1)TestCase测试用例
用户定义的TestCase必须扩展junit.framework.TestCase类,它以testXXX方法的形式包含了一个或多个测试。
一个测试用例把具有公共行为的测试归入一组。
(2)TestSuite测试套装
一个测试套装可以把多个测试用例或测试套装封装为一组
(3)TestRunner测试运行器
测试运行器用来运行测试套装,Junit提供良种典型的测试运行器:
junit.swingui.TestRunner和junit.textui.TestRunner
2.JUnit核心类
JUnit核心类及其简介如下表所示:
表1:
JUnit核心类
3.JUnit单元测试的步骤
(1)重载setUp(),封装测试环境初始化及测试数据准备;
(2)设计测试方法,以testXXX命名。
(3)在测试方法中使用断言方法如assertEquals(),assertTrue()等。
JUnit中断言方法如表2所示。
(4)设计测试套件,或使用缺省的测试套件,调用TestRunner执行测试脚本,生成测试结果;
(5)重载tearDown()析构测试环境,执行收尾动作
表2:
断言方法
assertEquals(expected,actual)
assertEquals(message,expected,actual)
assertEquals(expected,actual,delta)-usedondoublesorfloats,wheredeltaisthedifferenceinprecision
assertEquals(message,expected,actual,delta)-usedondoublesorfloats,wheredeltaisthedifferenceinprecision
assertFalse(condition)
assertFalse(message,condition)
assertNotNull(object)
assertNotNull(message,object)
assertNotSame(expected,actual)
assertNotSame(message,expected,actual)
assertNull(object)
assertNull(message,object)
assertSame(expected,actual)
assertSame(message,expected,actual)
assertTrue(condition)
assertTrue(message,condition)
案例分析
1.三角形问题
设计类Triangle及其测试类TestTriangle,其类图如图6所示。
Triangle中包含三个属性borderA,borderB,borderC,一个构造函数,isTriangle()判断三角形三边是否构成三角形,isType()判断在输入的三边形成三角形的情况下所形成的三角形的具体类型:
等边三角形、等腰三角形还是不等边三角形。
图6:
Triangle及其测试类
TestTriangle类定义如下:
(1)重载setUp()方法,进行测试准备,封装测试的预期结果。
(2)定义测试方法testIsTriangle()和testIsType()
(3)在测试方法中使用断言
(4)定义suite()方法,使用缺省的TestSuite
returnnewTestSuite(TestTriangle.class);
(5)通过缺省的TestSuite调用TestRunner生成测试结果。
//调用SWINGUI或TEXTUI执行测试
//junit.textui.TestRunner.run(TestTriangle.class);
junit.swingui.TestRunner.run(TestTriangle.class);
2.[作业]设计类NextDay,该类包含year,month,day三个属性,该类可以根据用户输入的year,month,day的值输出用户输入日期对应的下一天。
设计NextDay的测试类TestNextDay对其中核心方法,综合使用等价类划分和健壮性测试方法设计测试用例执行单元测试。
建议NextDay结构如图7所示。
图7:
NextDay
3.测试Controller构件
通过上述案例我们已经初步了解了JUnit单元测试的机制。
现在我们将通过一个控制器构件的测试,讨论如何定义测试需求、设计测试用例、实现和执行测试脚本。
控制器构件是一个常见的应用模式,它可以以多种方式出现在软件系统中,例如在Web应用中控制器构件接收HTTP请求、进行请求分发给适合的处理器构件。
(1)设计控制器构件
按照控制器构件的规格说明:
接收请求;
对请求执行常用计算;
选择合适的请求处理器;
路由请求;
可能会提供一个顶层的处理器用于处理错误或异常。
鉴于此我们设计4个对象(如图8所示):
Request,Response,RequestHandler,Controller。
Controller接受Request,分发给RequestHandler,并返回Response对象。
图8:
测试对象控制器构件
(2)定义控制器构件的实现基类
阅读《JUnitinAction》,按照图8编写DefaultController类;
设计错误访问类ErrotResponse,如图9所示。
图9:
ErrorResponse
(3)0次迭代
定义测试类框架,实例化DefaultController,此时测试方法定义为testMethod():
publicclassTestDefaultController0extendsTestCase{
privateDefaultControllercontroller;
//以setUp()为扩展点,实例化DefaultController对象
protectedvoidsetUp(){
controller=newDefaultController();
//首先定义新的测试方法,为了有测试可以运行
publicvoidtestMethod(){
/*为暂时没有实现的测试代码抛出异常,这样避免了测试通过,
*提醒必须实现其代码
thrownewRuntimeException("
Implementme!
"
(4)测试需求
Controller的目的在于处理请求并响应,但是在处理请求之前必须首先设计添加RequestHandler来做实际的处理工作。
所以要紧的是先测试是否可以添加RequestHandler。
为了判断测试是否成功需要比较测试预期结果和实际执行结果。
检查addHandler()发现:
voidaddHandler(Requestrequest,Responseresponse)
而DefaultController中getHandler()签名如下:
RequestHandlergetHandler(Requestrequest)
单元测试的要点在于一次只测试一个对象。
为了创建单元测试必须两种对象:
领域对象,代表被测试对象;
测试对象,即和领域对象交互的对象,他们是测试领域对象的环境需求。
在本轮迭代中,我们唯一用到的领域对象是DefaultController,其它都是测试对象。
鉴于此,设计TestDefaultController1如下:
增加一个RequestHandler,引用一个Request;
获得一个RequestHandler判断传递的是否是同一个Request;
检查获取的RequestHandler是否就是添加的那个。
那么,把测试类放在何方?
有两种可选方案,其一把测试类作为同一包中的公共类;
其二,作为testcase的内部类,如图10所示。
图10:
迭代1
迭代1TestDefaultController1的测试方法:
publicvoidtestAddHandler(){
//实例化测试对象
Requestrequest=newTestRequest();
RequestHandlerhandler=newTestHandler();
//把待测试对象controller添加到合适的测试处理器
controller.addHandler(request,handler);
RequestHandlerhandler2=controller.getHandler(request);
//监测读取的对象是否为刚刚传入的对象
assertSame(handler2,handler);
迭代2TestDefaultController2:
测试DefaultController的核心—请求处理
publicvoidtestProcessRequest(){
//创建测试对象,并添加测试处理器
//调用processRequest()
Responseresponse=controller.processRequest(request);
//验证reponse对象不是null
assertNotNull("
Mustnotreturnanullresponse."
response);
//把测试结果和预期的TestResponse对象进行比较
assertEquals(TestResponse.class,response.getClass());
迭代2代码重构TestDefaultController2Refact:
分离初始化逻辑到setUp()
TestDefaultController2Refact中定义属性:
privateRequestrequest;
privateRequestHandlerhandler;
setUp():
request=newTestRequest();
handler=newTestHandler();
同时修改testAddHandler()和testProcessHandler()
迭代3TestDefaultControllerMockExceptionCond:
模拟异常条件
在TestDefaultControllerMockExceptionCond创建内部类TestExceptionHandlerimplementsRequestHandler
privateclassTestHandlerimplementsRequestHandler{
publicResponseprocess(Requestrequest)throwsException{
returnnewTestResponse();
}
定义测试方法testProcessRequestAnswerErrorResponse():
publicvoidtestProcessRequestAnswerErrorResponse(){
TestRequestrequest=newTestRequest();
TestExceptionHandlerhandler=newTestExceptionHandler();
Mustnotreturnanullresponse"
response);
assertEquals(ErrorResponse.class,response.getClass());
运行测试脚本,测试方法testProcessRequestAnswerErrorResponse()运行失败如图11所示,我们需要做两件事情:
为测试请求换名,因为Fixture中已经有了一个叫做Test的请求;
其次,需要在类中增加更多的异常处理代码,以免运行时候发生RuntimeException。
图11:
testProcessRequestAnswerErrorResponse()运行失败
迭代3代码优化:
TestDefaultControllerMockExceptionCondEvolution
定义TestDefaultControllerMockExceptionCondEvolution的内部类
TestRequestimplementsRequest{
privatestaticfinalStringDEFAULT_NAME="
Test"
;
privateStringname;
publicTestRequest(Stringname){
this.name=name;
publicTestRequest(){
this(DEFAULT_NAME);
publicStringgetName(){
returnthis.name;
重构TestDefaultControllerMockExceptionCondEvolution中的testProcessAnswerErrorResponse:
TestRequestrequest=newTestRequest("
testError"
迭代4TestDefaultControllerException:
测试异常
测试异常情况如下:
其一,测试请求处理器为定义异常;
测试注册同名请求异常。
在TestDefaultControllerException添加测试方法testGetHandlerNotDefined()和testAddRequestDuplicateName()。
……
publicvoidtestGetHandlerNotDefined(){
testNotDefined"
try{
fail("
Anexceptionsholdberaisediftherequested"
+"
handlerhasnotbeenregistered."
}catch(RuntimeExceptionexception){
assertTrue(true);
publicvoidtestAddRequestDuplicateName(){
TestHandlerhandler=newTestHandler();
//Registrationofhandlerforthefirsttime.
//Registrationofhandlerforthesecondtime.
controller.addHandler(request,handler);
Anexceptionshouldberaisedifthedefault"
TestRequesthasalreadlybeenregistered."
经过多次迭代后得到的测试脚本的类图如图11所