实验五 1 使用JUnit执行单元测试.docx
- 文档编号:7653018
- 上传时间:2023-01-25
- 格式:DOCX
- 页数:18
- 大小:198.33KB
实验五 1 使用JUnit执行单元测试.docx
《实验五 1 使用JUnit执行单元测试.docx》由会员分享,可在线阅读,更多相关《实验五 1 使用JUnit执行单元测试.docx(18页珍藏版)》请在冰豆网上搜索。
实验五1使用JUnit执行单元测试
实践使用JUnit执行单元测试
通过一个简单的例子了解JUnit的使用。
控制器Controller:
同客户交互、控制并管理每个请求的处理的组件,既可用于表现层模式,也可以用于业务层模式。
控制器所做的事情:
●接受请求
●对请求执行常用计算
●选择合适的请求处理器
●路由请求,以使处理器可以执行相关的业务逻辑
●可能会提供一个顶层的处理器用于处理错误和异常
Controller是一个很好用的模式,许多应用中都可以看到它。
如:
在表现层模式中,一个WebController接受HTTP请求,并把HTTP参数、cookie、HTTP头取出,使得HTTP元素易于被其他部分访问。
WebController会基于请求中的元素判断需调用的合适的业务逻辑组件。
ApacheStruts框架就是WebController的一个例子。
这里假设一个简单的使用Controller情况:
用户向系统发送请求Request,系统中的控制器Controller经过分析Request中的信息,然后调用一个合适的服务,并将服务的处理结果Response返回给客户。
系统中所有注册的服务有一个统一的服务调用接口供控制Controller调用,将这些服务命名为RequestHandler。
因此系统的框架设计如下:
以下是对应的Java代码:
publicinterfaceRequest{
StringgetName();
}
publicinterfaceResponse{
}
publicinterfaceController{
ResponseprocessRequest(Requestrequest);
voidaddHandler(Requestrequest,RequestHandlerrequestHandler);
}
publicinterfaceRequestHandler{
Responseprocess(Requestrequest)throwsException;
}
下面是对接口的实现,DefaultController实现Controller接口,ErrorResponse实现了Response接口,大部分程序的后续工作就是对这个框架进行修补使其满足操作的需要。
具体实现代码如下:
importjava.util.HashMap;
importjava.util.Map;
publicclassDefaultControllerimplementsController{
privateMaprequestHandlers=newHashMap();
protectedRequestHandlergetHandler(Requestrequest){
if(!
this.requestHandlers.containsKey(request.getName())){
Stringmessage="Cannotfindhandlerforrequestname"+"["
+request.getName()+"]";
thrownewRuntimeException(message);
}
return(RequestHandler)this.requestHandlers.get(request.getName());
}
publicResponseprocessRequest(Requestrequest){
Responseresponse;
try{
response=getHandler(request).process(request);
}catch(Exceptionexception){
response=newErrorResponse(request,exception);
}
returnresponse;
}
publicvoidaddHandler(Requestrequest,RequestHandlerrequestHandler){
if(this.requestHandlers.containsKey(request.getName())){
thrownewRuntimeException("Arequesthandlerhas"
+"alreadybeenregisteredforrequestname"+"["
+request.getName()+"]");
}else{
this.requestHandlers.put(request.getName(),requestHandler);
}
}
}
publicclassErrorResponseimplementsResponse{
privateRequestoriginalRequest;
privateExceptionoriginalException;
publicErrorResponse(Requestrequest,Exceptionexception){
this.originalRequest=request;
this.originalException=exception;
}
publicRequestgetOriginalRequest(){
returnthis.originalRequest;
}
publicExceptiongetOriginalException(){
returnthis.originalException;
}
}
最终程序组织为:
下面使用JUnit来测试这个Controller是否正常工作。
在eclipse中新建项目,导入或输入上面的代码。
新建一个test源代码文件夹,将所有的测试代码都放在这个文件夹中。
新建一个JUnitTestCase,测试DefaultController类,命名为TestDefaultController1。
内容如下:
Next进入下一步,选择要测试的方法:
自动生成的代码如下:
importjunit.framework.TestCase;
publicclassTestDefalutController1extendsTestCase{
protectedvoidsetUp()throwsException{
super.setUp();
}
protectedvoidtearDown()throwsException{
super.tearDown();
}
publicfinalvoidtestAddHandler(){
fail("Notyetimplemented");
}
}
添加代码,使其成为第一个测试类,具体代码参考如下:
importjunit.framework.TestCase;
publicclassTestDefalutController1extendsTestCase{
privateControllercontroller;
protectedvoidsetUp()throwsException{
controller=newDefaultController();
}
protectedvoidtearDown()throwsException{
}
publicfinalvoidtestAddHandler(){
fail("Notyetimplemented");
}
}
点击RunRunAsJUnitTest,则得到失败的结果,因为现在还没有实现测试的逻辑。
下面开始实现测试的代码,第一个碰到的问题就是,用到的测试类(脚手架之类的东西)如何组织。
一种方案将这些类放在测试用例类中作为内部类;一种方案将其作为作为共有类。
原则是:
如果类比较简单,而且以后也不太可能变复杂,则将其写成内部类比较简单方便,否则将其作为外部的公共类。
此处我们将其写为内部类。
用到的测试相关的类有Request实现类,Response实现类,RequestHandler实现类。
结构如图:
具体代码如下,注意阴影段落中为新建的测试类:
importjunit.framework.TestCase;
publicclassTestDefaultController2extendsTestCase{
privateDefaultControllercontroller;
protectedvoidsetUp()throwsException{
controller=newDefaultController();
}
privateclassTestRequestimplementsRequest{
publicStringgetName(){
return"Test";
}
}
privateclassTestHandlerimplementsRequestHandler{
publicResponseprocess(Requestrequest)throwsException{
returnnewTestResponse();
}
}
privateclassTestResponseimplementsResponse{
//empty
}
publicvoidtestAddHandler(){
Requestrequest=newTestRequest();
RequestHandlerhandler=newTestHandler();
controller.addHandler(request,handler);
RequestHandlerhandler2=controller.getHandler(request);
assertSame(handler2,handler);
}
publicvoidtestProcessRequest(){
Requestrequest=newTestRequest();
RequestHandlerhandler=newTestHandler();
controller.addHandler(request,handler);
Responseresponse=controller.processRequest(request);
assertNotNull("Mustnotreturnanullresponse",response);
assertEquals(TestResponse.class,response.getClass());
}
}
点击RunRunAsJUnitTest,运行结果如下:
说明测试成功。
重点关注assert方法的使用。
注意蓝色底纹的代码,在两段中属于重复的代码,对于各个测试中重复的设置等操作可以将其抽出放入setUp中。
这样可以改善代码的设计。
因此改进的下一个版本如下:
importjunit.framework.TestCase;
publicclassTestDefaultController3extendsTestCase{
privateDefaultControllercontroller;
privateRequestrequest;
privateRequestHandlerhandler;
protectedvoidsetUp()throwsException{
controller=newDefaultController();
request=newTestRequest();
handler=newTestHandler();
controller.addHandler(request,handler);
}
privateclassTestRequestimplementsRequest{
publicStringgetName(){
return"Test";
}
}
privateclassTestHandlerimplementsRequestHandler{
publicResponseprocess(Requestrequest)throwsException{
returnnewTestResponse();
}
}
privateclassTestResponseimplementsResponse{
//empty
}
publicvoidtestAddHandler(){
RequestHandlerhandler2=controller.getHandler(request);
assertSame(handler2,handler);
}
publicvoidtestProcessRequest(){
Responseresponse=controller.processRequest(request);
assertNotNull("Mustnotreturnanullresponse",response);
assertEquals(TestResponse.class,response.getClass());
}
}
上面的测试中对TestResponse的检查停留在类的比较上,我们希望能对返回的响应中的内容进行判断,因此对TestResponse类进行改进,增加一个内容,简单表示返回的值:
重构后的TestResponse类如下:
privateclassTestResponseimplementsResponse{
privatestaticfinalStringNAME="Test";
publicStringgetName(){
returnNAME;
}
publicbooleanequals(Objectobject){
booleanresult=false;
if(objectinstanceofTestResponse){
result=((TestResponse)object).getName().equals(getName());
}
returnresult;
}
publicinthashCode(){
returnNAME.hashCode();
}
}
测试的逻辑中可以改为:
publicvoidtestProcessRequest(){
Responseresponse=controller.processRequest(request);
assertNotNull("Mustnotreturnanullresponse",response);
assertEquals(newTestResponse(),response);
}
到目前为止,测试都没有偏离执行的主路径。
如果待测对象之一的行为已未预期的方式发生了改变,如发生了异常,因此这类问题也需要检测。
模拟异常条件
异常testcase是单元测试的真正闪光之处。
单元测试可以模拟异常条件,而不是坐等异常的发生(如集成测试中)。
本例中在processRequest方法中已经有了先见之明,将发生错误的情况封装成一个ErrorResponse返回作为特殊的响应。
但是是否可能发生其他异常情况呢?
这里我们需要更多的考虑。
try{
response=getHandler(request).process(request);
}catch(Exceptionexception){
response=newErrorResponse(request,exception);
}
如何模拟异常条件以测试处理器是否正常工作呢?
为了测试正常请求处理,创建了TestRequestHandler,处理器返回TestRequest。
为了测试异常错误处理,创建一个TestExceptionHandler,这个处理器抛出一个异常。
privateclassTestExceptionHandlerimplementsRequestHandler{
publicResponseprocess(Requestrequest)throwsException{
thrownewException("errorprocessingrequest");
}
}
下面创建一个测试方法,注册这个处理器,并试图处理一个请求,如下:
publicvoidtestProcessRequestAnswersErrorResponse(){
TestRequestrequest=newTestRequest();
TestExceptionHandlerhandler=newTestExceptionHandler();
controller.addHandler(request,handler);
Responseresponse=controller.processRequest(request);
assertNotNull("Mustnotreturnanullresponse",response);
assertEquals(ErrorResponse.class,response.getClass());
}
上面测试中的代码与setUp中的很类似,与之前的代码一起运行时会发生问题。
即注册一个重名的请求,会抛出未声明的RuntimeException。
因此我们设计两个方法来检测这一类问题:
其中fail表示如果没有发生异常,则这个测试失败。
没有达到检测的目的。
这条语句防止异常没有发生。
捕获异常时,说明其名称为expected,表明这是成功的测试,因此异常处理中使用assertTrue(true)。
publicvoidtestGetHandlerNotDefined(){
TestRequestrequest=newTestRequest("testNotDefined");
try{
controller.getHandler(request);
fail("Anexceptionshouldberaisediftherequested"
+"handlerhasnotbeenregistered");
}catch(RuntimeExceptionexpected){
assertTrue(true);
}
}
publicvoidtestAddRequestDuplicateName(){
TestRequestrequest=newTestRequest();
TestHandlerhandler=newTestHandler();
try{
controller.addHandler(request,handler);
fail("Anexceptionshouldberaisedifthedefault"
+"TestRequesthasalreadybeenregistered");
}catch(RuntimeExceptionexpected){
assertTrue(true);
}
}
基于这样的考虑,对TestRequest进行重新设计,使其能处理更一般的情况。
即创建不同的请求。
privateclassTestRequestimplementsRequest{
privatestaticfinalStringDEFAULT_NAME="Test";
privateStringname;
publicTestRequest(Stringname){
this.name=name;
}
publicTestRequest(){
this(DEFAULT_NAME);
}
publicStringgetName(){
returnthis.name;
}
}
最终所有的测试代码如下:
importjunit.framework.TestCase;
publicclassTestDefaultControllerextendsTestCase{
privateDefaultControllercontroller;
privateRequestrequest;
privateRequestHandlerhandler;
protectedvoidsetUp()throwsException{
controller=newDefaultController();
request=newTestRequest();
handler=newTestHandler();
controller.addHandler(request,handler);
}
privateclassTestRequestimplementsRequest{
privatestaticfinalStringDEFAULT_NAME="Test";
privateStringname;
publicTestRequest(Stringname){
this.name=name;
}
publicTestRequest(){
this(DEFAULT_NAME);
}
publicStringgetName(){
returnthis.name
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实验五 使用JUnit执行单元测试 实验 使用 JUnit 执行 单元测试