Spring 3.0 Controller层单元测试

      今天在对基于Spring mvc架构的项目写单元测试的时候,本来想用@RunWith的方式轻松搞定它。不曾想还不是那么so easy,   一方面是controller层没有联系起来,再者就是SpringJUnit4ClassRunner启动就报不知道什么鬼错了。索性就换成mock方式,再熟悉一下spring容器加载机制也未尝不是一件好事~  废话少说,直接上代码先!!!

测试基类

   1:  package com.andy.test;
   2:   
   3:  import java.io.File;
   4:  import java.sql.Connection;
   5:  import java.sql.PreparedStatement;
   6:  import java.sql.ResultSet;
   7:  import java.sql.SQLException;
   8:  import java.util.Arrays;
   9:  import java.util.Map;
  10:   
  11:  import javacommon.util.StringUtils;
  12:   
  13:  import javax.servlet.http.HttpServletRequest;
  14:  import javax.servlet.http.HttpServletResponse;
  15:   
  16:  import org.apache.log4j.Logger;
  17:  import org.apache.log4j.PropertyConfigurator;
  18:  import org.junit.After;
  19:  import org.junit.Before;
  20:  import org.junit.Test;
  21:  import org.springframework.mock.web.MockHttpServletRequest;
  22:  import org.springframework.mock.web.MockHttpServletResponse;
  23:  import org.springframework.mock.web.MockServletContext;
  24:  import org.springframework.util.Assert;
  25:  import org.springframework.web.bind.annotation.RequestMethod;
  26:  import org.springframework.web.context.WebApplicationContext;
  27:  import org.springframework.web.context.support.XmlWebApplicationContext;
  28:  import org.springframework.web.servlet.HandlerAdapter;
  29:  import org.springframework.web.servlet.HandlerExecutionChain;
  30:  import org.springframework.web.servlet.HandlerMapping;
  31:  import org.springframework.web.servlet.ModelAndView;
  32:  import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
  33:  import org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping;
  34:   
  35:  import com.apabi.shop.dao.UserDao;
  36:   
  37:  /**
  38:   * 
  39:   * Spring MVC 3.0 Controller层测试基类
  40:   * 提供Controller层测试stub
  41:   * 
  42:   * @author Andy
  43:   * @since 2012.5.10 1:25 PM
  44:   * @version 1.0
  45:   *
  46:   */
  47:  @SuppressWarnings({"rawtypes" , "unchecked"})
  48:  public abstract class AbstractTestCase {
  49:      // 应用程序根配置文件(不包含applicationContext.xml配置文件)
  50:      private final static String[] ROOT_CONFIGLOCATION = {"classpath:/spring_test/*-test.xml"};
  51:  
  52:      // 随Servlet启动时加载配置文件。(默认为applicationContext.xml/servletName-servlet.xml)
  53:      private final static String DEFAULT_CONFIG_LOCATION = "classpath:/spring_test/applicationContext.xml";
  54:  
  55:      // 模块功能CRUD
  56:      protected final static String MODULE_QUERY = "query";
  57:      protected final static String MODULE_LIST = "list";
  58:      protected final static String MODULE_SHOW = "show";
  59:      protected final static String MODULE_CREATE = "create";
  60:      protected final static String MODULE_SAVE = "save";
  61:      protected final static String MODULE_EDIT = "edit";
  62:      protected final static String MODULE_DELETE = "delete";
  63:  
  64:      // id集合key
  65:      protected final static String FIELD_IDS_KEY = "ids";
  66:  
  67:  
  68:      // Logger
  69:      protected final static Logger logger;
  70:  
  71:      // 对象映射处理器(映射到controller类)
  72:      protected HandlerMapping handlerMapping;
  73:  
  74:      // 方法适配处理器(映射到具体方法)
  75:      protected HandlerAdapter handlerAdapter;
  76:  
  77:      // Spring容器上下文
  78:      protected XmlWebApplicationContext webApplicationContext;
  79:  
  80:      static{
  81:          // 清除旧的测试日志文件
  82:          File logFile = new File("logs/test.log");
  83:          if(logFile.exists()){
  84:              logFile.setReadable(true);
  85:              logFile.setWritable(true);
  86:              logFile.delete();
  87:          }
  88:  
  89:          // 加载测试日志配置文件
  90:          PropertyConfigurator.configure(AbstractTestCase.class.getResource("/log4j-test.properties"));
  91:          logger = Logger.getLogger(AbstractTestCase.class);
  92:      }
  93:  
  94:      @Before
  95:      public  void setUp(){
  96:          // 创建web应用程序根上下文,该上下文解析并管理系统实例
  97:          XmlWebApplicationContext rootWebApplicationContext = new XmlWebApplicationContext();
  98:          rootWebApplicationContext.setConfigLocations(ROOT_CONFIGLOCATION);
  99:          // 创建servletContext上下文
 100:          MockServletContext servletContext = new MockServletContext();
 101:          rootWebApplicationContext.setServletContext(servletContext);
 102:          rootWebApplicationContext.refresh();
 103:          //servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, rootWebApplicationContext);
 104:  
 105:          // 创建web应用程序上下文,管理controller层请求业务
 106:          webApplicationContext = new XmlWebApplicationContext();
 107:          webApplicationContext.setConfigLocation(DEFAULT_CONFIG_LOCATION);
 108:          webApplicationContext.setParent(rootWebApplicationContext);
 109:          webApplicationContext.refresh();
 110:  
 111:          servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, webApplicationContext);
 112:   
 113:  
 114:          // 获取默认mapping,adapter
 115:           handlerMapping = webApplicationContext.getBean(ControllerClassNameHandlerMapping.class);
 116:           handlerAdapter = webApplicationContext.getBean(AnnotationMethodHandlerAdapter.class);
 117:      }
 118:  
 119:      // TODO 测试流程   创建->修改->查询-> 删除
 120:      @Test
 121:      public void testMain(){
 122:          // 创建
 123:          this.testCreateUI();
 124:          this.testSave();
 125:  
 126:          // 修改
 127:          this.testEditUI();
 128:          this.testUpdate();
 129:  
 130:          // 其他操作
 131:          this.testOthers();
 132:  
 133:          // 查询
 134:          this.testQuery();
 135:          this.testList();
 136:          this.testShow();
 137:  
 138:          // 删除
 139:          this.testDelete();
 140:      }
 141:  
 142:      /**
 143:       * 跳转到创建UI
 144:       */
 145:      protected abstract void testCreateUI();
 146:  
 147:      /**
 148:       * 创建功能
 149:       */
 150:      protected abstract void testSave();
 151:  
 152:      /**
 153:       * 跳转到编辑UI
 154:       */
 155:      protected abstract void testEditUI();
 156:  
 157:      /**
 158:       * 修改功能
 159:       */
 160:      protected abstract void testUpdate();
 161:  
 162:      /**
 163:       * 按条件查询
 164:       */
 165:      protected abstract void testQuery();
 166:  
 167:      /**
 168:       * 分页查询所有记录
 169:       */
 170:      protected abstract void testList();
 171:  
 172:      /**
 173:       * 查询某条记录详情
 174:       */
 175:      protected abstract void testShow();
 176:  
 177:      /**
 178:       * 删除某条记录
 179:       */
 180:      protected abstract void testDelete();
 181:  
 182:      /**
 183:       * 其他操作
 184:       */
 185:      protected abstract void testOthers();
 186:   
 187:      @After
 188:      public void tearDown(){
 189:  
 190:      }
 191:  
 192:      // 查询相关
 193:      /**
 194:       * 查询所有数据记录
 195:       * 
 196:       * @param module    当前模块
 197:       * @param operate   当前操作
 198:       */
 199:      protected void list(String module, String operate){
 200:          ModelAndView view  = this.sendRequest(module + operate, null);
 201:          Assert.notNull(view);
 202:          logger.info(StringUtils.format("【Unit Testing Info】query {0}: {1}.", module , view));
 203:      }
 204:  
 205:      /**
 206:       * 查询某个记录详情
 207:       * @param module    当前模块
 208:       * @param operate   当前操作
 209:       * @param params    某记录数据参数
 210:       * @param method    请求类型
 211:       */
 212:      protected void show(String module, String operate , Map<String,String> params , RequestMethod method){
 213:          Assert.notEmpty(params, "The request params is null!");
 214:          ModelAndView view = this.sendRequest(module + operate, params, method);
 215:          Assert.notNull(view);
 216:          logger.info(StringUtils.format("【Unit Testing Info】 query {0} by params {1}: {2}.", module , params, view));
 217:      }
 218:  
 219:      /**
 220:       * 多条件查询数据
 221:       * @param module    当前模块
 222:       * @param operate   当前操作
 223:       * @param params    条件数据参数
 224:       * @param method    请求类型
 225:       */
 226:      protected void query(String module, String operate , Map<String,String> params , RequestMethod method){
 227:          ModelAndView view = this.sendRequest(module + operate, params, method);
 228:          Assert.notNull(view);
 229:          logger.info(StringUtils.format("【Unit Testing Info】 query {0} by params {1}: {2}.", module , params, view));
 230:      }
 231:  
 232:      // 创建修改相关
 233:      /**
 234:       * 请求待创建页面
 235:       * 
 236:       * @param module    当前模块
 237:       * @param operate   当前操作
 238:       */
 239:      protected void create(String module, String operate){
 240:          ModelAndView view = this.sendRequest(module + operate, null);
 241:          Assert.notNull(view);
 242:          logger.info(StringUtils.format("【Unit Testing Info】 {0} create url: {1}.", module , view));
 243:      }
 244:  
 245:      /**
 246:       * 请求待修改页面
 247:       * @param module    当前模块
 248:       * @param operate   当前操作
 249:       * @param params    待数据参数
 250:       */
 251:      protected void edit(String module, String operate , Map<String,String> params){
 252:          Assert.notEmpty(params, "The request params is null!");
 253:          ModelAndView view = this.sendRequest(module + operate, params);
 254:          Assert.notNull(view);
 255:          logger.info(StringUtils.format("【Unit Testing Info】 {0} edit url: {1}.", module , view));
 256:      }
 257:  
 258:      /**
 259:       * 创建/修改数据请求
 260:       * @param module    当前模块
 261:       * @param operate   当前操作
 262:       * @param params    创建/修改数据参数
 263:       * @param method    请求类型
 264:       */
 265:      protected void saveOrModify(String module, String operate , Map<String,String> params , RequestMethod method){
 266:          Assert.notEmpty(params, "The request params is null!");
 267:          ModelAndView view = this.sendRequest(module + operate, params , method);
 268:          Assert.notNull(view);
 269:          logger.info(StringUtils.format("【Unit Testing Info】 {0} save {1} success.", module , params));
 270:      }
 271:  
 272:      // 删除操作
 273:      /**
 274:       * 删除数据请求
 275:       * @param module    当前模块
 276:       * @param operate   当前操作
 277:       * @param params    删除数据参数
 278:       */
 279:      protected void delete(String module, String operate , Map<String,String[]> params){
 280:          Assert.notEmpty(params, "The request params is null!");
 281:          ModelAndView view = this.sendRequest(module + operate, params);
 282:          Assert.notNull(view);
 283:          logger.info(StringUtils.format("【Unit Testing Info】 {0} delete {1} success", module , Arrays.asList(params.get("ids"))));
 284:      }
 285:  
 286:      /**
 287:       * 发送请求信息
 288:       * 
 289:       * @see AbstractTestCase#processRequest
 290:       * 
 291:       * @param uri        请求uri
 292:       * @param params    请求参数
 293:       * @param method    请求类型  默认请求类型为GET
 294:       *                  eg POST GET PUT DELETE HEAD TRACE OPTIONS
 295:       */
 296:      protected ModelAndView sendRequest(String uri, Map params,  RequestMethod... method){
 297:          ModelAndView view = null;
 298:          try {
 299:              view = this.processRequest(uri, params, method);
 300:              if(null == view){
 301:                  view = new ModelAndView();
 302:              }
 303:          } catch (Exception e) {
 304:              // 由于Spring Mock Request没有与线程绑定,故忽略异常信息“No thread-bound request found”
 305:              if(null != e && e.getMessage().contains("No thread-bound request found")){
 306:                  view = new ModelAndView();
 307:              }else{
 308:                  logger.error("Testing fail info:", e);
 309:              }
 310:          }
 311:          return view;
 312:      }
 313:  
 314:  
 315:      /**
 316:       * 处理用户请求
 317:       * 
 318:       * 实现构造请求数据,并发送请求,处理请求业务
 319:       * 
 320:       * @param uri        请求uri
 321:       * @param method    请求类型  默认请求类型为GET
 322:       *                  eg POST GET PUT DELETE HEAD TRACE OPTIONS
 323:       * @param params    请求参数
 324:       * @return            请求结果
 325:       * @throws Exception 
 326:       */
 327:      private ModelAndView processRequest(String uri , Map params , RequestMethod... method ) throws Exception{
 328:          HttpServletRequest request = this.createRequest(uri, params);
 329:          HttpServletResponse response = new MockHttpServletResponse();
 330:          return this.execute(request, response);
 331:      }
 332:  
 333:      /**
 334:       * 执行请求
 335:       * @param request    http请求对象
 336:       * @param response  http响应对象
 337:       * @return            UI信息
 338:       * @throws Exception 
 339:       */
 340:      private ModelAndView execute(HttpServletRequest request , HttpServletResponse response) throws Exception{
 341:          HandlerExecutionChain chain = handlerMapping.getHandler(request);
 342:          return handlerAdapter.handle(request, response, chain.getHandler());
 343:      }
 344:  
 345:      /**
 346:       * 封装请求信息
 347:       * @param uri        请求uri
 348:       * @param method    请求类型  默认请求类型为GET
 349:       *                  eg POST GET PUT DELETE HEAD TRACE OPTIONS
 350:       * @param params    请求参数
 351:       * @return          http请求对象
 352:       */
 353:      private HttpServletRequest createRequest(String uri , Map<String , String> params , RequestMethod... method ){
 354:          if(null == uri || uri.isEmpty()){
 355:              throw new IllegalArgumentException("It must contains request uri!");
 356:          }
 357:          // 构造请求
 358:          MockHttpServletRequest request = new MockHttpServletRequest();
 359:          request.setRequestURI(uri);
 360:          if(null == method  || method.length == 0){
 361:              request.setMethod(RequestMethod.GET.name());
 362:          }else{
 363:              request.setMethod(method[0].name());
 364:          }
 365:          if(null != params && !params.isEmpty()){
 366:              request.addParameters(params);
 367:          }
 368:          return request;
 369:      }
 370:  
 371:      /**
 372:       * 获取当前主键ID
 373:       * @param con    连接对象
 374:       * @return        当前主键
 375:       */
 376:      protected long getCurrentPrimaryKeyId(String sql){
 377:          long primaryKey = -1;
 378:          PreparedStatement pstmt = null;
 379:          ResultSet rs = null;
 380:          try {
 381:              UserDao baseIbatisDao = webApplicationContext.getBean(UserDao.class);
 382:              Connection con = baseIbatisDao.db().getDataSource().getConnection();
 383:              pstmt = con.prepareStatement(sql);
 384:              rs = pstmt.executeQuery();
 385:              if(rs.next()){
 386:                  String number = String.valueOf(rs.getObject(1));
 387:                  if(StringUtils.isNumeric(number)){
 388:                      primaryKey = Long.valueOf(number) - 1;
 389:                  }
 390:              }
 391:          } catch (SQLException e) {
 392:              e.printStackTrace();
 393:          }finally{
 394:              try {
 395:                  if(null != rs){
 396:                      rs.close();
 397:                  }
 398:                  if(null != pstmt){
 399:                      pstmt.close();
 400:                  }
 401:              } catch (SQLException e) {
 402:                  e.printStackTrace();
 403:              }
 404:          }
 405:  
 406:          return primaryKey;
 407:      }
 408:   
 409:  }

单元测试用例类

   1:  package com.andy.test.unit;
   2:   
   3:  import java.util.Collections;
   4:  import java.util.HashMap;
   5:  import java.util.Map;
   6:   
   7:  import org.springframework.web.bind.annotation.RequestMethod;
   8:   
   9:  import com.andy.dao.UserDao;
  10:  import com.andy.dao.RoleDao;
  11:  import com.andy.dao.RightsDao;
  12:  import com.andy.model.User;
  13:  import com.andy.model.Role;
  14:  import com.andy.model.Rights;
  15:  import com.andy.test.AbstractTestCase;
  16:   
  17:  /**
  18:   * 用户管理模块测试用例
  19:   * 
  20:   * @author Andy
  21:   * @since 2012.5.10 04:45 PM
  22:   * @version 1.0
  23:   */
  24:  @SuppressWarnings("unchecked")
  25:  public class UserManagerTestCase extends AbstractTestCase{
  26:  
  27:      // 用户管理包含子模块
  28:      private final static String USER_MODULE = "/user/";
  29:      private final static String ROLR_MODULE = "/role/";
  30:      private final static String RIGHTS_MODULE = "/rights/";
  31:  
  32:      // 测试id
  33:      private Long userTestId;
  34:      private Long roleTestId;
  35:      private Long rightsTestId;
  36:  
  37:      @Override
  38:      protected void testCreateUI() {
  39:          // TODO Auto-generated method stub
  40:   
  41:      }
  42:   
  43:      @Override
  44:      protected void testSave() {
  45:          // TODO Auto-generated method stub
  46:   
  47:      }
  48:   
  49:      @Override
  50:      protected void testEditUI() {
  51:          // TODO Auto-generated method stub
  52:   
  53:      }
  54:   
  55:      @Override
  56:      protected void testUpdate() {
  57:          // TODO Auto-generated method stub
  58:   
  59:      }
  60:   
  61:      @Override
  62:      protected void testQuery() {
  63:          // TODO Auto-generated method stub
  64:   
  65:      }
  66:   
  67:      @Override
  68:      protected void testList() {
  69:          // TODO Auto-generated method stub
  70:   
  71:      }
  72:   
  73:      @Override
  74:      protected void testShow() {
  75:          // TODO Auto-generated method stub
  76:   
  77:      }
  78:   
  79:      @Override
  80:      protected void testDelete() {
  81:          // TODO Auto-generated method stub
  82:   
  83:      }
  84:  }

转载于:https://my.oschina.net/andy0807/blog/87049

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值