在本章中,我们为一个简单但完整的应用程序controller创建了一个测试用例。测试用例并不是测试单个的组件,而是检验多个组例,如何一起工作。我们从一个可以用于任何类的简单测试用例开始.然后把新的测试逐个添加到测试用例中,直到所有初始的组件都被测试到。由于断言变得越来越复杂、因此我们通过Hamcrest匹配器找到了一种简化断言的方法。我们预期这个包会日益增长,所以我们为测试类创建了另一个源代码目录。因为测试源代码和领域源代码的目录都位于同一个包中,所以我们仍然可以测试受保护成员和默认的包成员。
1.被测部分:Controller模式
1.1 Controller简介
一般而言, Controller可以处理以下事务
- 接受请求
- 根据请求执行任意常用极端
- 选择 一个合适的请求处理器
- 路由请求,以便处理器可以执行相关的业务逻辑
- 可能提供一个顶层处理器来处理错误和异常
1.2 设计接口
Controller模式中涉及四个角色:
- Request
- Response
- RequestHandler
- Controller
Controller接受一个Request,分发给一个RequestHandler,并返回一个Response对象。
//首先,定义一个 Request 接口,这个接口只有一个返问请求的唯一名称的getName方法
public interface Request{
String getName();
}
//其次指定一个空接口。要开始编写代码,你只需要返回一个 Response对象即可。Response 对象所封装的是你可以稍后处理的内容。
public interface Response{
}
//接下来,定义一个能够处理 Request 并返回 Response 的 RequestHandle,RequestHandle是一个辅助组件,被设计用来处理大部分的“肮脏工作”。它可以调用各种类,这些类可能抛出任意类型的异常。Exception就是由process万法抛出的。
public interface RequestHandler{
Response procees(Request request) throws Exception;
}
//定义一个顶层方法来处理收到的请求。在接受请求之后, controller将请求分发给相应的RequestHandler 。
public interface Controller{
Response process(Request request);
//add Handler 方法允许你扩展Controller,而无须修改 Java原代码。
void addHandler(Request request,RequestHandler requestHandler)
}
controller的目的是处理一个请求并返回一个响应。但是,在你处理一个请求之前,设计要求添加一个RequestHandler来做这个处理。
package com.JUnittTest.mastery;
import java.util.HashMap;
import java.util.Map;
public class DefaultController {
// 请求处理器注册表,对每一个request注册对应的requestHandler
private Map requestHandlers=new HashMap();
// 声明一个受保护的方法,为接受的请求获取RequestHandler
protected RequestHandler getHandler(Request request){
if(!this.requestHandlers.containsKey(request.getName())){
String message="Cannot find handler for request name "+"["+request.getName()+"]";
throw new RuntimeException(message);
}
// 向调用者返回相应的requestHandler
return (RequestHandler)this.requestHandlers.get(request.getName());
}
// 是Controller类的核心,把response分派给相应的requestHandler,并传回requestHandler的response
public Response processRequest(Request request){
Response response;
try {
// getHandler(request)返回一个RequestHandler接口类型的对象
// RequestHandler接口定义了process(request)方法,返回一个response对象
response=getHandler(request).process(request);
} catch (Exception exception) {
response=new ErrorResponse(request,exception);
}
return response;
}
// 检查requestHandler是否已经被注册
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);
}
}
}
我们还需要额外再定义一个ErrorPesponse接口,不同于posponse接口,ErrorPesponse接口返回的是错误的posponse。
package com.JUnittTest.mastery;
public interface Response {
String getName();
}
2. 设计单元测试
2.1 测试前部署 @Before @BeforeClass
@Before @After 注释方法会在每个@Test方法前后执行
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Before {
}
@BeforeClass @AfterClass 注释方法会在只会在所有@Test方法前后执行一次
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BeforeClass {
}
从源代码分析,@Before @BeforeClass 注释非常简单,没有内部属性,只是起到对代码运行顺序的引导。(@After @AfterClass也是一样的)
一般在@BeforeClass @AfterClass注释方法中的代码完成测试环境的部署和拆除。
在@Before @After注释方法中的代