欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring 3.0 Controller层单元测试

程序员文章站 2022-03-02 18:12:31
...

      今天在对基于Spring mvc架构的项目写单元测试的时候,本来想用@RunWith的方式轻松搞定它。不曾想还不是那么so easy,Spring 3.0 Controller层单元测试   一方面是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