打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
基于TestNG+Mockito及自动装配注解的Spring MVC集成测试
本文主要总结自己近期在项目中对MVC集成测试的实践及理解,因为先前对这块未实践过。主要参考了官方文档《11.3.6 Spring MVC Test Framework》这一章节内容,涉及到 Spring TestContext FrameworkTestNG Mockito 这3个测试框架,完全基于Spring自动装配注解(@Autowired)实现,不需要定义额外的setter或构造器来注入bean,也不需要通过Mockito的@Mock和MockitoAnnotations.initMocks(this)代码方式实现实例化,而是通过静态工厂方法Mockito.mock(...)在XML中实现bean实例初始化。

 

废话不多说了,看一下需要几步就能搞定MVC Controller与Service层的集成测试。(如果你现在也正好使用Spring Test框架,可以看看下面对TestNG基类封装的代码,我觉得自己写得还可以。小小赞美一下啦~)

 

1. 定义底层Service接口及实现

 

Java代码  
  1. /** 
  2.  * User service. 
  3.  */  
  4. public interface UserService {  
  5.   
  6.     /** 
  7.      * Gets user info for specified user ID. 
  8.      *  
  9.      * @param id user ID 
  10.      * @return 
  11.      */  
  12.     User getUserInfo(long id);  
  13.       
  14.     /** 
  15.      * Updates user info. 
  16.      *  
  17.      * @param user user info 
  18.      * @return -1 means fail, 0 means success. 
  19.      */  
  20.     int updateUserInfo(User user);  
  21.   
  22. }  

 

Java代码  
  1. /** 
  2.  * User query service. 
  3.  */  
  4. public interface UserQueryService {  
  5.   
  6.     /** 
  7.      * Gets user name for specified user ID. 
  8.      *  
  9.      * @param userId user ID 
  10.      * @return 
  11.      */  
  12.     String getUserName(long userId);  
  13.       
  14.     /** 
  15.      * Updates user name for specified user ID. 
  16.      *  
  17.      * @param userId 
  18.      * @param userName 
  19.      * @return -1 means fail, 0 means success. 
  20.      */  
  21.     int updateUserName(long userId, String userName);  
  22.   
  23. }  

 

Java代码  
  1. /** 
  2.  * User query service implementation. 
  3.  */  
  4. @Service  
  5. public class UserQueryServiceImpl implements UserQueryService {  
  6.   
  7.     @Autowired  
  8.     private UserService userService;  
  9.   
  10.     @Override  
  11.     public String getUserName(long userId) {  
  12.         User user = this.userService.getUserInfo(userId);  
  13.         return user != null ? user.getName() : "";  
  14.     }  
  15.   
  16.     @Override  
  17.     public int updateUserName(long userId, String userName) {  
  18.         User user = new User(userId, userName);  
  19.         int udpateResult = this.userService.updateUserInfo(user);  
  20.         return udpateResult;  
  21.     }  
  22.   
  23. }  

 

 

2. 为 Controller 层的每一接口定义一对 Request与Response(可重用的,就别多定义啦!~\(≧▽≦)/~)

Java代码  
  1. /** 
  2.  * Base request info. 
  3.  * 
  4.  * @author  Bert Lee 
  5.  * @version 2014-8-19 
  6.  */  
  7. @JsonIgnoreProperties(ignoreUnknown = true) // 忽略多传的参数  
  8. public class BaseRequest {  
  9.   
  10. }  

 

Java代码  
  1. /** 
  2.  * User ID request info. 
  3.  * 
  4.  * @author  Bert Lee 
  5.  * @version 2014-8-19 
  6.  */  
  7. public class UserIDRequest extends BaseRequest {  
  8.   
  9.     @JsonProperty("id")  
  10.     @NotNull(message = "id param is null")  
  11.     @Min(value = 1, message = "id param must be great or equal than \\{{value}\\}") // 4.3. Message interpolation -《JSR 303: Bean Validation》  
  12.     protected long id;  
  13.   
  14.     public long getId() {  
  15.         return id;  
  16.     }  
  17.   
  18.     public void setId(long id) {  
  19.         this.id = id;  
  20.     }  
  21.   
  22.     @Override  
  23.     public String toString() {  
  24.         return "UserIDRequest [id=" + id + "]";  
  25.     }  
  26.   
  27. }  

 

Java代码  
  1. /** 
  2.  * User name response info. 
  3.  */  
  4. public class UserNameResponse {  
  5.   
  6.     @JsonProperty("name")  
  7.     protected String name = "";  
  8.   
  9.     public String getName() {  
  10.         return name;  
  11.     }  
  12.   
  13.     public void setName(String name) {  
  14.         this.name = name;  
  15.     }  
  16.   
  17.     @Override  
  18.     public String toString() {  
  19.         return "UserNameResponse [name=" + name + "]";  
  20.     }  
  21.   
  22. }  

 

Java代码  
  1. /** 
  2.  * User info request info. 
  3.  */  
  4. public class UserInfoRequest extends UserIDRequest {  
  5.   
  6.     @JsonProperty("name")  
  7.     @NotNull(message = "name param is null")  
  8.     @Size(min = 1, message = "name param is empty")  
  9.     protected String userName; // 变量名与请求参数名不一样,在@RequestBody中用到  
  10.   
  11.     public String getUserName() {  
  12.         return userName;  
  13.     }  
  14.   
  15.     public void setUserName(String userName) {  
  16.         this.userName = userName;  
  17.     }  
  18.   
  19.     @Override  
  20.     public String toString() {  
  21.         return "UserInfoRequest [userName=" + userName + ", id=" + id + "]";  
  22.     }  
  23.   
  24. }  

 

Java代码  
  1. /** 
  2.  * User modify result response info. 
  3.  */  
  4. public class UserResultResponse {  
  5.   
  6.     @JsonProperty("ret")  
  7.     protected int result;  
  8.       
  9.     @JsonProperty("ret_msg")  
  10.     protected String resultMessage;  
  11.   
  12.     public UserResultResponse() {  
  13.         this.result = 0;  
  14.         this.resultMessage = "ok";  
  15.     }  
  16.   
  17.     public int getResult() {  
  18.         return result;  
  19.     }  
  20.   
  21.     public void setResult(int result) {  
  22.         this.result = result;  
  23.     }  
  24.   
  25.     public String getResultMessage() {  
  26.         return resultMessage;  
  27.     }  
  28.   
  29.     public void setResultMessage(String resultMessage) {  
  30.         this.resultMessage = resultMessage;  
  31.     }  
  32.   
  33.     @Override  
  34.     public String toString() {  
  35.         return "UserResultResponse [result=" + result + ", resultMessage="  
  36.                 + resultMessage + "]";  
  37.     }  
  38.   
  39. }  

 

 

3. 实现 Controller 层逻辑

Java代码  
  1. /** 
  2.  * User Controller. 
  3.  * 
  4.  * @author  Bert Lee 
  5.  * @version 2014-8-19 
  6.  */  
  7. @Controller  
  8. @RequestMapping(value = "/user")  
  9. public class UserController {  
  10.   
  11.     @Autowired  
  12.     private UserQueryService userQueryService;  
  13.       
  14.       
  15.     @RequestMapping(value = "/get_user_name", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)  
  16.     @ResponseBody  
  17.     public UserNameResponse getUserName(@Valid UserIDRequest userIDRequest) {  
  18.         long userId = userIDRequest.getId();  
  19.         String userName = this.userQueryService.getUserName(userId);  
  20.           
  21.         UserNameResponse response = new UserNameResponse();  
  22.         if (!StringUtils.isEmpty(userName)) {  
  23.             response.setName(userName);  
  24.         }  
  25.           
  26.         return response;  
  27.     }  
  28.       
  29.     @RequestMapping(value = "/update_user_name", method = RequestMethod.POST,  
  30.             consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)  
  31.     @ResponseBody  
  32.     public UserResultResponse updateUserName(@Valid @RequestBody UserInfoRequest userInfoRequest) { // JSON request body map  
  33.         UserResultResponse response = new UserResultResponse();  
  34.           
  35.         long userId = userInfoRequest.getId();  
  36.         String userName = userInfoRequest.getUserName();  
  37.         int result = this.userQueryService.updateUserName(userId, userName);  
  38.         if (result < 0) {  
  39.             response.setResult(result);  
  40.             response.setResultMessage("update operation is fail");  
  41.         }  
  42.           
  43.         return response;  
  44.     }  
  45.   
  46. }  

 

 

4. 实现一个Service与Controller层的抽象测试基类(用于集成TestNG与MVC Test框架,且自动加载配置文件)

Java代码  
  1. /** 
  2.  * Abstract base test class for TestNG. 
  3.  * 
  4.  * @author  Bert Lee 
  5.  * @version 2014-8-19 
  6.  */  
  7. @ContextConfiguration("classpath:META-INF/spring/test-context.xml") // 集成应用上下文并加载默认的beans XML配置  
  8. public abstract class AbstractTestNGTest extends AbstractTestNGSpringContextTests { // 集成TestNG  
  9.   
  10.     /** 
  11.      * Initializes the test context. 
  12.      */  
  13.     @BeforeSuite(alwaysRun = true)  
  14.     public void init() {  
  15. //      MockitoAnnotations.initMocks(this); // 基于Spring自动装配注解,这里不再需要初始化  
  16.     }  
  17.   
  18. }  

 

Java代码  
  1. /** 
  2.  * Abstract controller tier base test class for TestNG. 
  3.  * 
  4.  * @author  Bert Lee 
  5.  * @version 2014-8-19 
  6.  */  
  7. @WebAppConfiguration("src/test/java") // 集成Web应用上下文  
  8. public abstract class AbstractControllerTestNGTest extends AbstractTestNGTest {  
  9.   
  10.     /** 
  11.      * MVC mock 
  12.      */  
  13.     protected MockMvc mockMvc;  
  14.       
  15.     /** 
  16.      * Gets the tested controller. 
  17.      *  
  18.      * @return the controller that is tested 
  19.      */  
  20.     protected abstract Object getController();  
  21.       
  22.     /** 
  23.      * Setups the tested controller in MVC Mock environment. 
  24.      */  
  25.     @BeforeClass(alwaysRun = true)  
  26.     public void setup() {  
  27.         this.mockMvc = MockMvcBuilders.standaloneSetup(this.getController()).build();  
  28.     }  
  29.       
  30.     /** 
  31.      * Mocks the GET request. 
  32.      *  
  33.      * @param url 
  34.      * @param params 
  35.      * @param expectedContent 
  36.      * @throws Exception 
  37.      */  
  38.     protected void getMock(String url, Object[] params, String expectedContent) throws Exception {  
  39.         // 2. 构造GET请求  
  40.         MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders  
  41.                 .get(url, params);  
  42.           
  43.         this.jsonRequestMock(requestBuilder, expectedContent);  
  44.     }  
  45.       
  46.     /** 
  47.      * Mocks the POST request. 
  48.      *  
  49.      * @param url 
  50.      * @param paramsJson 
  51.      * @param expectedContent 
  52.      * @throws Exception 
  53.      */  
  54.     protected void postMock(String url, String paramsJson, String expectedContent) throws Exception {  
  55.         // 2. 构造POST请求  
  56.         MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders  
  57.                 .post(url)  
  58.                 .content(paramsJson) // 设置request body请求体,服务于"@RequestBody"  
  59.                 ;  
  60.           
  61.         this.jsonRequestMock(requestBuilder, expectedContent);  
  62.     }  
  63.       
  64.     /** 
  65.      * Mocks the request for "application/json;charset=UTF-8" media type (Content-Type). 
  66.      *  
  67.      * @param requestBuilder 
  68.      * @param expectedContent 
  69.      * @throws Exception 
  70.      */  
  71.     private void jsonRequestMock(MockHttpServletRequestBuilder requestBuilder, String expectedContent) throws Exception {  
  72.         // 2. 设置HTTP请求属性  
  73.         requestBuilder.contentType(MediaType.APPLICATION_JSON)  
  74.                 .accept(MediaType.APPLICATION_JSON)  
  75.                 .characterEncoding(CharEncoding.UTF_8)  
  76.                 ;  
  77.           
  78.         // 3. 定义期望响应行为  
  79.         this.mockMvc.perform(requestBuilder)  
  80.                 .andDo(print()) // 打印整个请求与响应细节  
  81.                 .andExpect(status().isOk())  
  82.                 .andExpect(content().contentType(MediaType.APPLICATION_JSON))  
  83.                 .andExpect(content().string(expectedContent)) // 校验是否是期望的结果  
  84.                 ;  
  85.     }  
  86.   
  87. }  

 

5. 实现Controller与Service层的测试逻辑

Java代码  
  1. /** 
  2.  * Test for {@link UserController}. 
  3.  * 
  4.  * @author  Bert Lee 
  5.  * @version 2014-8-19 
  6.  */  
  7. public class UserControllerTest extends AbstractControllerTestNGTest {  
  8.   
  9.     // tested controller  
  10.     @Autowired  
  11.     private UserController userControllerTest;  
  12.       
  13.     // mocked service (被依赖的服务)  
  14.     @Autowired  
  15.     private UserQueryService userQueryService;  
  16.       
  17.       
  18.     @Test(dataProvider = "getUserName")  
  19.     public void getUserName(Object[] params, String userName, String expectedContent) throws Exception {  
  20.         // 1. 定义"被依赖的服务"的方法行为  
  21.         when(this.userQueryService.getUserName(anyLong())).thenReturn(userName);  
  22.           
  23.         this.getMock("/user/get_user_name?id={id}", params, expectedContent);  
  24.     }  
  25.     @DataProvider(name = "getUserName")  
  26.     protected static final Object[][] getUserNameTestData() {  
  27.         Object[][] testData = new Object[][] {  
  28.                 { new Object[] { "23" }, "Bert Lee", "{\"name\":\"Bert Lee\"}" },  
  29.         };  
  30.         return testData;  
  31.     }  
  32.       
  33.     @Test(dataProvider = "updateUserName")  
  34.     public void updateUserName(String paramsJson, Integer result, String expectedContent) throws Exception {  
  35.         // 1. 定义"被依赖的服务"的方法行为  
  36.         when(this.userQueryService.updateUserName(anyLong(), anyString())).thenReturn(result);  
  37.           
  38.         this.postMock("/user/update_user_name", paramsJson, expectedContent);  
  39.     }  
  40.     @DataProvider(name = "updateUserName")  
  41.     protected static final Object[][] updateUserNameTestData() {  
  42.         Object[][] testData = new Object[][] {  
  43.                 { "{\"id\":23,\"name\":\"Bert Lee\"}", 0, "{\"ret\":0,\"ret_msg\":\"ok\"}" },  
  44.         };  
  45.         return testData;  
  46.     }  
  47.       
  48.     @Override  
  49.     public Object getController() {  
  50.         return this.userControllerTest;  
  51.     }  
  52.   
  53. }  

 

Java代码  
  1. /** 
  2.  * Test for {@link UserQueryService}. 
  3.  * 
  4.  * @author  Bert Lee 
  5.  * @version 2014-7-25 
  6.  */  
  7. public class UserQueryServiceTest extends AbstractTestNGTest {  
  8.   
  9.     // tested service  
  10.     @Autowired  
  11.     private UserQueryService userQueryServiceTest;  
  12.       
  13.     // mocked service (被依赖的服务)  
  14.     @Autowired  
  15.     private UserService userService;  
  16.       
  17.       
  18.     @Test(dataProvider = "getUserName")  
  19.     public void getUserName(User user, String expected) {  
  20.         // 1. 定义"被依赖的服务"的方法行为  
  21.         when(userService.getUserInfo(anyLong())).thenReturn(user);  
  22.           
  23.         String userName = this.userQueryServiceTest.getUserName(3L);  
  24.         assertEquals(userName, expected);  
  25.     }  
  26.     @DataProvider(name = "getUserName")  
  27.     protected static final Object[][] getUserNameTestData() {  
  28.         Object[][] testData = new Object[][] {  
  29.                 { null, "" },  
  30.                 { new User(3L, ""), "" },  
  31.                 { new User(10L, "Edward Lee"), "Edward Lee" },  
  32.                 { new User(23L, "李华刚@!~#$%^&"), "李华刚@!~#$%^&" },  
  33.         };  
  34.         return testData;  
  35.     }  
  36.   
  37. }  

 

6. 定义XML bean配置文件,实现测试对象及被依赖的服务的自动注入

Xml代码  
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.       xsi:schemaLocation="  
  5.       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd ">  
  6.   
  7.     <bean id="userControllerTest" class="com.weibo.web.UserController" />  
  8.     <!-- 被依赖的服务 -->  
  9.     <bean id="userQueryService" class="org.mockito.Mockito" factory-method="mock">  
  10.       <constructor-arg value="com.weibo.service.UserQueryService" />  
  11.     </bean>  
  12.       
  13.     <bean id="userQueryServiceTest" class="com.weibo.service.impl.UserQueryServiceImpl" />  
  14.     <!-- 被依赖的服务 -->  
  15.     <bean id="userService" class="org.mockito.Mockito" factory-method="mock">  
  16.       <constructor-arg value="com.weibo.service.UserService" />  
  17.     </bean>  
  18.       
  19. </beans>  

 

7. 运行测试用例,OK!

 

7步搞定,挺简单的吧,O(∩_∩)O哈哈~

 

玩得开心!^_^

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Struts2 ONGL表达式
微服务框架Spring Cloud介绍 Part3: Mysteam项目结构与开发用户注册服务
Android static静态成员变量的使用误区
关于Struts-JSON配置(详解带实例struts2的json数据支持)
Struts2 继承ActionSupport的用处
ognl总结
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服