掌握Junit

1、Controller组件定义

controller(控制器):同用户交互,控制并管理每个请求的处理,可用于表现层模式,也可用于业务层模式

control主要做这些事:接受请求、对请求执行常用计算、选择合适的请求处理器、路由请求,以便处理器可以执行相关的业务逻辑、可能会提供一个顶层的处理器用于处理错误和异常

设计接口:4个对象,Request、Response、RequestHandle、Controller

它们的关系是:Controller接受Request,分发给RequestHandler,并返回Response

那么先来编写一些初步接口:

public interface Request {String getName();}
//返回一个请求的名称,以便区分不同的请求。

public interface Response { }
//Response对象封装的是稍后可以处理的东西,目前只要满足签名

public interface RequestHandler {
  Response process(Request request)throws Exception;}
//定义一个能处理一个request并返回Response的反应RequestHandler。
//RequestHandler是一个辅助性组件,设计来做最“肮脏”的工作。它调用可能会抛出任何类型异常的类。
public interface Controller {
	Response processRequest(Request request);
//定义一个用来处理收到的请求的方法。
	void addHandler(Request request,RequestHandler requestHadnler);
//addHadndler方法允许扩展Controller而不必修改Java源代码。
}

一个简单的controller类的初步实现

public class DefaultController  implements Controller{
//声明一个HashMap,用作请求处理器的注册表;
    private Map requestHandlers=new HashMap();
    //增加一个保护方法getHandler,用来为指定的请求获取HequestHandler.
    protected RequestHandler getHandler(Request request) {
    //如果RequestHandler没有注册,那么抛出RuntimeException异常	if(!this.requestHandlers.containsKey(request.getName())){
		String message="Cannot find handler for request name"
				+"["+request.getName()+"]";
			throw new RuntimeException(message);
		}
		//返回适当的处理器来调用。
    return (RequestHandler) this.requestHandlers.get(request.getName());
	}


/*把请求分派到合适的处理器,并且把处理器的Response传回给调用者,如异常,捕获ErrorResponse*/
@Override
public Response processRequest(Request request) {
	Response response;
		try {response=getHandler(request).process(request);
		} catch (Exception e) {
		response=new ErrorResponse(request,e);}
		return response;
}


@Override
public void addHandler(Request request, RequestHandler requestHandler) {
	//检查处理器的名字是否已注册过,若已注册过,抛出异常。
	if(this.requestHandlers.containsKey(request.getName())){
 throw new RuntimeException("A request handler has already been "
		+"registered for request name ["+request.getName()+"]");
		}else{
	this.requestHandlers.put(request.getName(), requestHandler);
		}
	}
}

ErrorResponse类的定义

public class ErrorResponse implements Response {
	private Request originalRequest;
	private Exception originalException;
	public ErrorResponse(Request request, Exception exception) {
		this.originalRequest = request;
		this.originalException = exception;
	}
	public Request getOriginalRequest() {
		return this.originalRequest;
	}
	public Exception getOriginalException() {
		return this.originalException;
	}
}

contrler骨架的需求与代码相关

需求

解决方案

接受请求

public Response processRequest(Request request)

选择处理器

this.requestHandlers.get(request.getName())

路由请求

response = getRequestHandler(request).process(request);

错误处理

子类化ErrorRsponse

Controller组件测试

public class TestDefaultController {
	private DefaultController controller;
	
	@Before
	public void setUp(){
		controller=new DefaultController();
	}
	@Test
	public void testMethod(){
		fail("Not yet implemented");	
      }

}

添加handler

1、添加一个RequestHandler,引用一个Request

2、获得到一个RequestHandler传递的是同一个Request

3、检查得到RequestHandler,是否就是添加的那一个

public class TestDefaultController {
	....		
   //作为内部类的测试类
	private class TestRequest implements Request{
		public String getName(){
			return "test";}
	}
	private class TestHandler implements RequestHandler{
	public Response process(Request request) throws Exception {
			return new TestResponse();}
	}
	private class TestResponse implements Response{
	//暂时为空	
	}
}

在TestDefaultController添加testAddHander测试方法

@Test
	public void testAddHandler() {
	Request request = new TestRequest(); // 实例化对象
	RequestHandler handler = new TestHandler(); // 实例化对象
// 下面是的代码是测试目标:controller( 待测对象)添加一个测试handler,
	// 请注意controller是由setUp方法实例化来的。
	controller.addHandler(request, handler);
	// 把Handler读回到新的变量中。
	RequestHandler handler2 = controller.getHandler(request);
	// 对比两个对象是否是同一个对象。
	assertSame("Handler we set in controller should be the same handler we get",handler2, handler);
	}

controller的核心功能--处理请求

@Test
public void testProcessRequest(){
	Request request=new TestRequest();
   //创建测试对象并添加测试处理器
	RequestHandler handler=new TestHandler();
	controller.addHandler(request,handler);
	Response response=controller.processRequest(request);
	assertNotNull("Must not return a null response",response);
	assertEquals(TestResponse.class,response.getClass());
    }

•随着测试的深入,应该注意到测试应遵循如下的步骤:

–在开始测试时把环境设置成已知状态(创建对象、获取资源)。测试前的状态常称为Test fixture.

–调用待测试的方法。

–确认结果正确,通过调用一个或多个assert方法来实现。

分离出初始化逻辑

public class TestDefaultController {
	private DefaultController controller;
	private Request request;
	private RequestHandler handler ;

	@Before
	public void setUp() {
		controller = new DefaultController();
		 request = new TestRequest(); // 实例化对象
		 handler = new TestHandler(); // 实例化对象
		
		 controller.addHandler(request,handler);
	}

改进testProcessRequest

•为了检查两个不同的对象是否具有相同的标识,需要提供对标识的定义。

下面对TestResponse进行重构

private class TestResponse implements Response {
          private static final String NAME="Test";
          public String getName(){ return NAME; }

          public  boolean equals(Object object){
        	  boolean result=false;
        	  if(object instanceof TestResponse){
        		  result=((TestResponse )object).getName().equals(getName());
        	  }
        	  return result;
          }
          public int hashCode(){  return NAME.hashCode(); }
}

TestResponse有了自己的标识,现在就可以改进test方法

@Test
public void testProcessRequest(){
	Response response=controller.processRequest(request);
	assertNotNull("Must not return a null response",response);
	//assertEquals(TestResponse.class,response.getClass()); 改进
	assertEquals(new TestResponse(),response);
	}

在上面的编码阶段中,我们把错误处理代码放到了基类中,processRequest 方法捕获了所有的异常并返回特殊响应

try {		
            response=getHandler(request).process(request);
	} catch (Exception e) {
		response=new ErrorResponse(request,e);
     }

如何测试这个异常

为了测试错误情况,可以创建一个TestExceptionHandler内部类,用于抛出一个异常

private class TestExceptionHandler implements RequestHandler{
@Override
public Response process(Request request) throws Exception { throw new Exception("error processing request!");}
}

下面创建一个测试方法,注册这个Handler,并试图处理一个请求

//测试异常
	@Test
	public void testProcessRequestAnswertErrorResponse(){
		TestRequest request =new TestRequest();
		TestExceptionHandler handler=new TestExceptionHandler();
		controller.addHandler(request, handler);
		Response response =controller.processRequest(request);
		assertNotNull("Must not return a null response",response);
		assertEquals(ErrorResponse.class,response.getClass());
	}

运行后发现报错

错误的主要原因有两个

1、需要为测试请求换个名字。因为fixture中已经有了一个叫“test”请求

2、可能需要在类中增加更多异常处理代码,以免在实际运行时抛出RuntimeException异常

现在改进代码:

对于第一点:可以试着用fixtrue中请求对象,而不是自己创建。不过这样做还是会出现同样的错误。如果从fixture中删除注册默认TsetRequest和TestHandler的代码,那么就会为其他方法强入了冗余

最好的方法是:修改TestRequest,让他可以以不同的名字实例化

修改TestRequest,可以以不同的名字实例化

private class TestRequest implements Request {
		private static final String DEFAULT_NAME="Test";
		private String name;
		public TestRequest(String name){
			this.name=name;}
		public TestRequest(){
			this(DEFAULT_NAME);}
		public String getName() {
			return this.name;
		}
	}

在tsetProcessRequestAnswersErrorResponse中调用新的构造方法,这样异常请求就不会和fixture冲突

@Test
	public void testProcessRequestAnswertErrorResponse(){
		//TestRequest request =new TestRequest();
		TestRequest request =new TestRequest("testError");
		TestExceptionHandler handler=new TestExceptionHandler();
		controller.addHandler(request, handler);
		Response response =controller.processRequest(request);
		assertNotNull("Must not return a null response",response);
		assertEquals(ErrorResponse.class,response.getClass());
	}

①如果试图注册一个重名的请求,那么在测试时,会发出addHandler抛出了一个未说明的RuntimeException.

②再仔细观察一下代码,不难发现getHandler处理一个未被注册的Handler同样会抛出runtimeException.

③何如测试这两个方法呢?

可以这样做:

①插入应当抛出异常的语句

②在它后面加上fail语 句(以防万一没有抛出异常)

③捕捉预期的异常,把异常起名为expected,这样就很容易猜出这个异常是预期的

一切正常。

@Test
	public void testGetHandlerNotDefined(){
		TestRequest request=new TestRequest("testNotDefined");
		try {
		       controller.getHandler(request);
		       fail("An exception should be raised if the requested"
					+" handler has not been registered");
		} catch (RuntimeException expected) {
			assertTrue(true);
		}
	}
@Test
public void testAddRequestDuplicateName(){
	TestRequest request=new TestRequest();
	TestHandler handler=new TestHandler();
	try {
		controller.addHandler(request, handler);
		fail("An exception should be raised if the requested"
				+" handler has not been registered");
		} catch (RuntimeException expected) {
			assertTrue(true);
		}
	}
@Test(expected=RuntimeException.class)
	public void testGetHandlerNotDefined(){
		TestRequest request=new TestRequest("testNotDefined");
		controller.getHandler(request);		
	}
@Test(expected=RuntimeException.class)
	public void testAddRequestDuplicateName(){
		TestRequest request=new TestRequest();
		TestHandler handler=new TestHandler();
		controller.addHandler(request, handler);		
	}

有时我们对代码的执行效率有一定的要求。如果执行时间超过了某个值,那么就认为这样的代码是有问题的。

在@Test这个annotation里这一参数:timeout可以用于这方面的测试。

@Test(timeout=130)
	public void testProcessMultipleRequestTimeout(){
		Request request;
		Response response=new TestResponse();
		RequestHandler handler=new TestHandler();
		for(int i=0;i<99999;i++){
		request =new TestRequest(String.valueOf(i));
			controller.addHandler(request, handler);
		response=controller.processRequest(request);
			assertNotNull(response);		assertNotSame(ErrorResponse.class,response.getClass());
		}
	}
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Position 5

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值