Spring 3.0 Controller层单元测试
程序员文章站
2022-03-02 18:12:31
...
今天在对基于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
推荐阅读
-
idea创建一个入门Spring Boot项目(controller层)使用Moven代码管理
-
Spring Boot从Controller层进行单元测试的实现
-
在spring boot的controller层里使用cookie
-
spring boot使用自定义注解+AOP实现对Controller层方法的日志记录
-
easyui 1.5+spring 4.2+mybatis 3.4 Web示例(六) 单表操作之Controller层
-
记录一个Spring MVC Controller层,跳转到前端页面以及返回参数的包装类
-
spring3.0 单元测试
-
Spring Cloud Stream 3.0 单元测试
-
Spring MVC Controller 单元测试
-
Spring 3.0 单元测试