基于Spring MVC(REST API)做单元测试(mockito)

来源:http://blog.csdn.net/victor_cindy1/article/details/52126161


最近在公司用的Spring Mvc REST API框架做了一个项目,并且做了基于Spring的单元测试,今天先讲一下基于Spring框架的单元测试,测试使用的是Spring自带的test组件,再结合Mockito一起编写测试案例,以下示例会包括Controller和Service,由于Repository没有自己的逻辑,所以这里就不涉及Repository的单元测试。

首先看一下RestController的代码:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.dhb.springmvc.controller;  
  2.   
  3. import com.dhb.springmvc.entity.User;  
  4. import com.dhb.springmvc.service.UserService;  
  5. import org.springframework.beans.factory.annotation.Autowired;  
  6. import org.springframework.web.bind.annotation.*;  
  7.   
  8.   
  9. /** 
  10.  * Created by ${denghb} on 2016/7/31. 
  11.  */  
  12. @RestController  
  13. @RequestMapping("/springmvc")  
  14. public class UserController {  
  15.   
  16.     @Autowired  
  17.     private UserService userService;  
  18.   
  19.     @RequestMapping(value = "/{name}", method = RequestMethod.GET)  
  20.     public String sayHello(@PathVariable String name) {  
  21.         return name;  
  22.     }  
  23.   
  24.   
  25.     @RequestMapping(value = "/api/addUser", method = RequestMethod.POST)  
  26.     public int addUserInfo(@RequestBody User user) {  
  27.         int result = userService.addUser(user);  
  28.         return result;  
  29.     }  
  30.   
  31.     @RequestMapping(value = "/api/getUser/{id}", method = RequestMethod.GET)  
  32.     public User getUserInfo(@PathVariable int id) {  
  33.         return userService.findOneUser(id);  
  34.     }  
  35.   
  36.     @RequestMapping(value = "/api/registerUser", method = RequestMethod.POST)  
  37.     public Object registerUser(@RequestBody User user) {  
  38.         return userService.register(user);  
  39.     }  
  40.   
  41. }  

Service的功能代码,代码也比较简单,就是调用Repository做一些增删改查的动作。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.dhb.springmvc.service;  
  2.   
  3. import com.dhb.springmvc.entity.User;  
  4. import com.dhb.springmvc.repository.UserDao;  
  5. import org.springframework.beans.factory.annotation.Autowired;  
  6. import org.springframework.stereotype.Service;  
  7.   
  8. /** 
  9.  * Created by ${denghb} on 2016/8/2. 
  10.  */  
  11. @Service  
  12. public class UserService {  
  13.   
  14.     @Autowired  
  15.     UserDao userDao;  
  16.   
  17.     public int addUser (User user) {  
  18.         return userDao.addUser(user);  
  19.     }  
  20.   
  21.     public User findOneUser(int id) {  
  22.         return userDao.findOneUser(id);  
  23.     }  
  24.   
  25.     public String register(User user) {  
  26.         User user2 = userDao.findUserByName(user.getName());  
  27.         if(user2 == null) {  
  28.             userDao.addUser(user);  
  29.             return "成功";  
  30.         } else {  
  31.             return "失败";  
  32.         }  
  33.     }  
  34. }  

下面是repository代码:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.dhb.springmvc.repository;  
  2.   
  3. import com.dhb.springmvc.entity.User;  
  4. import org.springframework.beans.factory.annotation.Autowired;  
  5. import org.springframework.jdbc.core.BeanPropertyRowMapper;  
  6. import org.springframework.jdbc.core.JdbcTemplate;  
  7. import org.springframework.jdbc.core.RowMapper;  
  8. import org.springframework.stereotype.Repository;  
  9.   
  10. import javax.sql.DataSource;  
  11.   
  12. /** 
  13.  * Created by ${denghb} on 2016/8/2. 
  14.  */  
  15. @Repository  
  16. public class UserDao {  
  17.     private DataSource dataSource;  
  18.     private JdbcTemplate jdbcTemplate;  
  19.   
  20.     @Autowired  
  21.     public void setDataSource(DataSource dataSource) {  
  22.         this.dataSource = dataSource;  
  23.         this.jdbcTemplate = new JdbcTemplate(dataSource);  
  24.     }  
  25.   
  26.     public int addUser(User user) {  
  27.         String name = user.getName();  
  28.         String password = user.getPassword();  
  29.         RowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);  
  30.         int result = jdbcTemplate.update("insert into user(name, password) values(?,?)",name, password);  
  31.         return result;  
  32.     }  
  33.   
  34.     public User findOneUser(int id) {  
  35.         RowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);  
  36.         User user = jdbcTemplate.queryForObject("select id, name, password from user where id = ?", mapper, id);  
  37.         return user;  
  38.     }  
  39.   
  40.     public User findUserByName(String name) {  
  41.         RowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);  
  42.         User user = jdbcTemplate.queryForObject("select id, name, password from user where name = ?", mapper, name);  
  43.         return user;  
  44.     }  
  45.   
  46. }  

entity类:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.dhb.springmvc.entity;  
  2.   
  3.   
  4. /** 
  5.  * Created by ${denghb} on 2016/8/1. 
  6.  */  
  7. //@XmlRootElement(name = "demo")  
  8. //@XmlRootElement  
  9. public class User {  
  10.     int id;  
  11.     String name;  
  12.     String password;  
  13.   
  14.     public User() {  
  15.   
  16.     }  
  17.   
  18.     public User(String name, String password) {  
  19.         this.name = name;  
  20.         this.password = password;  
  21.     }  
  22.   
  23.     public User(int id, String name, String password) {  
  24.         this.id = id;  
  25.         this.name = name;  
  26.         this.password = password;  
  27.     }  
  28.   
  29.     //@XmlElement  
  30.     public int getId() {  
  31.         return id;  
  32.     }  
  33.   
  34.     public void setId(int id) {  
  35.         this.id = id;  
  36.     }  
  37.   
  38.     //@XmlElement  
  39.     public String getName() {  
  40.         return name;  
  41.     }  
  42.   
  43.     public void setName(String name) {  
  44.         this.name = name;  
  45.     }  
  46.   
  47.     //@XmlElement  
  48.     public String getPassword() {  
  49.         return password;  
  50.     }  
  51.   
  52.     public void setPassword(String password) {  
  53.         this.password = password;  
  54.     }  
  55.   
  56.   
  57. }  

增加一个C3P0配置:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.dhb.springmvc.config;  
  2.   
  3. import com.mchange.v2.c3p0.ComboPooledDataSource;  
  4. import org.springframework.context.annotation.Bean;  
  5. import org.springframework.context.annotation.Configuration;  
  6. import org.springframework.context.annotation.PropertySource;  
  7.   
  8. /** 
  9.  * Created by ${denghb} on 2016/8/2. 
  10.  */  
  11. @Configuration  
  12. //@PropertySource(value = {"classpath:c3p0.properties"})  
  13. public class C3P0DataSourceBuilder {  
  14.   
  15.     /** 
  16.      * 配置数据源 
  17.      * @return 
  18.      */  
  19.     @Bean(name = "dataSource")  
  20.     public ComboPooledDataSource getDataSource() {  
  21.         try {  
  22.   
  23.             ComboPooledDataSource dataSource = new ComboPooledDataSource();  
  24.             dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/demo");  
  25.             dataSource.setDriverClass("com.mysql.jdbc.Driver");  
  26.             dataSource.setUser("root");  
  27.             dataSource.setPassword("123456");  
  28.             dataSource.setMaxPoolSize(75);  
  29.             return dataSource;  
  30.         } catch (Exception e) {  
  31.             return null;  
  32.         }  
  33.     }  
  34. }  

先看对应的RestController测试:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.dhb.springmvc.controller;  
  2.   
  3. import com.dhb.springmvc.config.DhbWebApplicationInitializer;  
  4. import com.dhb.springmvc.entity.User;  
  5. import com.dhb.springmvc.service.UserService;  
  6. import org.junit.Before;  
  7. import org.junit.Test;  
  8. import org.junit.runner.RunWith;  
  9. import org.mockito.InjectMocks;  
  10. import org.mockito.Mock;  
  11. import org.mockito.MockitoAnnotations;  
  12. import org.springframework.test.context.ContextConfiguration;  
  13. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
  14. import org.springframework.test.context.web.WebAppConfiguration;  
  15. import org.springframework.test.web.servlet.MockMvc;  
  16. import org.springframework.test.web.servlet.setup.MockMvcBuilders;  
  17.   
  18. import static org.junit.Assert.assertEquals;  
  19. import static org.mockito.Mockito.verify;  
  20. import static org.mockito.Mockito.when;  
  21. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;  
  22. import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;  
  23. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;  
  24. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;  
  25.   
  26. /** 
  27.  * Created by ${denghb} on 2016/8/2. 
  28.  */  
  29. @RunWith(SpringJUnit4ClassRunner.class)  
  30. @WebAppConfiguration  
  31. @ContextConfiguration(classes = {DhbWebApplicationInitializer.class})  
  32. public class UserControllerTest {  
  33.     private MockMvc mockMvc;  
  34.   
  35.     @Mock  
  36.     private UserService userService;  
  37.   
  38.     @InjectMocks  
  39.     private UserController userController;  
  40.   
  41.     @Before  
  42.     public void setup() {  
  43.         MockitoAnnotations.initMocks(this);  
  44.         this.mockMvc = MockMvcBuilders.standaloneSetup(userController).build();  
  45.     }  
  46.   
  47.   
  48.     @Test  
  49.     public void testAdd() {  
  50.         int id = 1;  
  51.         String name = "邓海波";  
  52.         String password = "123456";  
  53.   
  54.         User user = new User();  
  55.         user.setId(id);  
  56.         user.setName(name);  
  57.         user.setPassword(password);  
  58.         when(userService.addUser(user)).thenReturn(1);  
  59.   
  60.         int restUser = userController.addUserInfo(user);  
  61.         assertEquals(1, restUser);  
  62.   
  63.         verify(userService).addUser(user);  
  64.     }  
  65.   
  66.     @Test  
  67.     public void testGetUserInfo() throws Exception {  
  68.         int userId = 1;  
  69.         String userName = "邓海波";  
  70.         String userPassword = "123456";  
  71.   
  72.         User user = new User();  
  73.         user.setId(userId);  
  74.         user.setName(userName);  
  75.         user.setPassword(userPassword);  
  76.         when(userService.findOneUser(userId)).thenReturn(user);  
  77.   
  78.         mockMvc.perform(get("/springmvc/api/getUser/{id}", userId))  
  79.                 .andDo(print())  
  80.                 .andExpect(status().isOk())  
  81.                 .andExpect(jsonPath("id").value(userId))  
  82.                 .andExpect(jsonPath("name").value(userName))  
  83.                 .andExpect(jsonPath("password").value(userPassword));  
  84.   
  85.         verify(userService).findOneUser(userId);  
  86.     }  
  87.   
  88. }  

首先是Spring的几个Annotate
RunWith(SpringJUnit4ClassRunner.class): 表示使用Spring Test组件进行单元测试;
WebAppConfiguration: 使用这个Annotate会在跑单元测试的时候真实的启一个web服务,然后开始调用Controller的Rest API,待单元测试跑完之后再将web服务停掉;
ContextConfiguration: 指定Bean的配置文件信息,可以有多种方式,这个例子使用的是文件路径形式,如果有多个配置文件,可以将括号中的信息配置为一个字符串数组来表示;
然后是Mockito的Annotate
Mock: 如果该对象需要mock,则加上此Annotate;
InjectMocks: 使mock对象的使用类可以注入mock对象,在上面这个例子中,mock对象是MailService,使用了MailService的是MailController,所以在Controller加上该Annotate;
Setup方法
MockitoAnnotations.initMocks(this): 将打上Mockito标签的对象起作用,使得Mock的类被Mock,使用了Mock对象的类自动与Mock对象关联。
mockMvc: 细心的朋友应该注意到了这个对象,这个对象是Controller单元测试的关键,它的初始化也是在setup方法里面。
Test Case
首先mock了MailService的send方法,让其返回一个成功的Result对象。
mockMvc.perform: 发起一个http请求。
post(url): 表示一个post请求,url对应的是Controller中被测方法的Rest url。
param(key, value): 表示一个request parameter,方法参数是key和value。
andDo(print()): 表示打印出request和response的详细信息,便于调试。
andExpect(status().isOk()): 表示期望返回的Response Status是200。
andExpect(content().string(is(expectstring)): 表示期望返回的Response Body内容是期望的字符串。
使用print打印处理的信息类似下面显示的内容:

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. MockHttpServletRequest:  
  2.          HTTP Method = GET  
  3.          Request URI = /springmvc/api/getUser/1  
  4.           Parameters = {}  
  5.              Headers = {}  
  6.   
  7.              Handler:  
  8.                 Type = com.dhb.springmvc.controller.UserController  
  9.               Method = public com.dhb.springmvc.entity.User com.dhb.springmvc.controller.UserController.getUserInfo(int)  
  10.   
  11.                Async:  
  12.    Was async started = false  
  13.         Async result = null  
  14.   
  15.   Resolved Exception:  
  16.                 Type = null  
  17.   
  18.         ModelAndView:  
  19.            View name = null  
  20.                 View = null  
  21.                Model = null  
  22.   
  23.             FlashMap:  
  24.   
  25. MockHttpServletResponse:  
  26.               Status = 200  
  27.        Error message = null  
  28.              Headers = {Content-Type=[application/json;charset=UTF-8]}  
  29.         Content type = application/json;charset=UTF-8  
  30.                 Body = {"id":1,"name":"邓海波","password":"123456"}  
  31.        Forwarded URL = null  
  32.       Redirected URL = null  
  33.              Cookies = []  

再来看一下service对应的测试代码:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.dhb.springmvc.service;  
  2.   
  3. import com.dhb.springmvc.config.DhbWebApplicationInitializer;  
  4. import com.dhb.springmvc.entity.User;  
  5. import com.dhb.springmvc.repository.UserDao;  
  6. import org.junit.Before;  
  7. import org.junit.Test;  
  8. import org.junit.runner.RunWith;  
  9. import org.mockito.InjectMocks;  
  10. import org.mockito.Mock;  
  11. import org.mockito.MockitoAnnotations;  
  12. import org.springframework.test.context.ContextConfiguration;  
  13. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
  14.   
  15. import static org.hamcrest.Matchers.is;  
  16. import static org.junit.Assert.assertThat;  
  17. import static org.mockito.Matchers.any;  
  18. import static org.mockito.Matchers.anyString;  
  19. import static org.mockito.Mockito.verify;  
  20. import static org.mockito.Mockito.when;  
  21.   
  22. /** 
  23.  * Created by ${denghb} on 2016/8/3. 
  24.  */  
  25. @RunWith(SpringJUnit4ClassRunner.class)  
  26. @ContextConfiguration(classes = {DhbWebApplicationInitializer.class})  
  27. public class UserServiceTest {  
  28.     @Mock  
  29.     private UserDao userDao;  
  30.   
  31.     @InjectMocks  
  32.     private UserService userService;  
  33.   
  34.     @Before  
  35.     public void setup() {  
  36.         MockitoAnnotations.initMocks(this);  
  37.     }  
  38.   
  39.     @Test  
  40.     public void testRegister() {  
  41.         when(userDao.findUserByName(anyString())).thenReturn(null);  
  42.         when(userDao.addUser(any(User.class))).thenReturn(0);  
  43.   
  44.         assertThat(userService.register(new User("邓海波""567890")), is("成功"));  
  45.   
  46.         verify(userDao).findUserByName(anyString());  
  47.         verify(userDao).addUser(any(User.class));  
  48.     }  
  49. }  

Service的单元测试就比较简单了,大部分内容都在RestController里面讲过,不同的地方就是RestController是使用mockMvc对象来模拟Controler的被测方法,而在Service的单元测试中则是直接调用Service的方法。

这是pom.xml文件

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
  3.   <modelVersion>4.0.0</modelVersion>  
  4.   <groupId>com.dhb.demo</groupId>  
  5.   <artifactId>spring4MVCHelloWorldNoXMLDemo</artifactId>  
  6.   <packaging>war</packaging>  
  7.   <version>0.1.0-SNAPSHOT</version>  
  8.   <name>spring4MVCHelloWorldNoXMLDemo Maven Webapp</name>  
  9.   <url>http://maven.apache.org</url>  
  10.   
  11.   <properties>  
  12.     <jetty.context>/</jetty.context>  
  13.     <jetty.http.port>9089</jetty.http.port>  
  14.     <jetty.https.port>9444</jetty.https.port>  
  15.     <jetty.stopPort>10081</jetty.stopPort>  
  16.   
  17.     <spring.version>4.1.4.RELEASE</spring.version>  
  18.   </properties>  
  19.   
  20.   <dependencies>  
  21.     <!-- spring -->  
  22.     <dependency>  
  23.       <groupId>org.springframework</groupId>  
  24.       <artifactId>spring-web</artifactId>  
  25.       <version>${spring.version}</version>  
  26.     </dependency>  
  27.     <dependency>  
  28.       <groupId>org.springframework</groupId>  
  29.       <artifactId>spring-webmvc</artifactId>  
  30.       <version>${spring.version}</version>  
  31.     </dependency>  
  32.     <dependency>  
  33.       <groupId>org.springframework</groupId>  
  34.       <artifactId>spring-expression</artifactId>  
  35.       <version>${spring.version}</version>  
  36.     </dependency>  
  37.     <dependency>  
  38.       <groupId>org.springframework</groupId>  
  39.       <artifactId>spring-context-support</artifactId>  
  40.       <version>${spring.version}</version>  
  41.     </dependency>  
  42.     <dependency>  
  43.       <groupId>org.springframework</groupId>  
  44.       <artifactId>spring-test</artifactId>  
  45.       <version>${spring.version}</version>  
  46.     </dependency>  
  47.     <dependency>  
  48.       <groupId>org.springframework</groupId>  
  49.       <artifactId>spring-jdbc</artifactId>  
  50.       <version>${spring.version}</version>  
  51.     </dependency>  
  52.   
  53.   
  54.     <!-- junit -->  
  55.     <dependency>  
  56.       <groupId>junit</groupId>  
  57.       <artifactId>junit</artifactId>  
  58.       <version>4.12</version>  
  59.       <scope>test</scope>  
  60.     </dependency>  
  61.   
  62.     <!-- jackson -->  
  63.     <dependency>  
  64.       <groupId>org.codehaus.jackson</groupId>  
  65.       <artifactId>jackson-mapper-asl</artifactId>  
  66.       <version>1.9.13</version>  
  67.     </dependency>  
  68.     <dependency>  
  69.       <groupId>com.fasterxml.jackson.core</groupId>  
  70.       <artifactId>jackson-databind</artifactId>  
  71.       <version>2.3.4</version>  
  72.     </dependency>  
  73.   
  74.     <dependency>  
  75.       <groupId>javax.servlet</groupId>  
  76.       <artifactId>javax.servlet-api</artifactId>  
  77.       <version>3.1.0</version>  
  78.     </dependency>  
  79.     <dependency>  
  80.       <groupId>javax.servlet.jsp</groupId>  
  81.       <artifactId>javax.servlet.jsp-api</artifactId>  
  82.       <version>2.3.1</version>  
  83.     </dependency>  
  84.     <dependency>  
  85.       <groupId>javax.servlet</groupId>  
  86.       <artifactId>jstl</artifactId>  
  87.       <version>1.2</version>  
  88.     </dependency>  
  89.   
  90.     <!-- mockito -->  
  91.     <dependency>  
  92.       <groupId>org.mockito</groupId>  
  93.       <artifactId>mockito-core</artifactId>  
  94.       <version>1.10.19</version>  
  95.       <scope>test</scope>  
  96.     </dependency>  
  97.   
  98.     <!-- jsonPath -->  
  99.     <dependency>  
  100.       <groupId>com.jayway.jsonpath</groupId>  
  101.       <artifactId>json-path</artifactId>  
  102.       <version>2.2.0</version>  
  103.     </dependency>  
  104.     <dependency>  
  105.       <groupId>com.jayway.jsonpath</groupId>  
  106.       <artifactId>json-path-assert</artifactId>  
  107.       <version>2.2.0</version>  
  108.     </dependency>  
  109.   
  110.     <!--c3po-->  
  111.     <dependency>  
  112.       <groupId>c3p0</groupId>  
  113.       <artifactId>c3p0</artifactId>  
  114.       <version>0.9.1.2</version>  
  115.     </dependency>  
  116.   
  117.     <dependency>  
  118.       <groupId>mysql</groupId>  
  119.       <artifactId>mysql-connector-java</artifactId>  
  120.       <version>5.1.6</version>  
  121.     </dependency>  
  122.   
  123.   </dependencies>  
  124.   
  125.   
  126.   
  127.   <build>  
  128.     <plugins>  
  129.       <plugin>  
  130.         <groupId>org.apache.maven.plugins</groupId>  
  131.         <artifactId>maven-compiler-plugin</artifactId>  
  132.         <version>3.1</version>  
  133.         <configuration>  
  134.           <source>1.7</source>  
  135.           <target>1.7</target>  
  136.           <showWarnings>true</showWarnings>  
  137.         </configuration>  
  138.       </plugin>  
  139.   
  140.       <!--maven jetty 插件配置-->  
  141.       <plugin>  
  142.         <groupId>org.eclipse.jetty</groupId>  
  143.         <artifactId>jetty-maven-plugin</artifactId>  
  144.         <version>9.2.17.v20160517</version>  
  145.         <configuration>  
  146.           <webApp>  
  147.             <contextPath>${jetty.context}</contextPath>  
  148.           </webApp>  
  149.           <httpConnector>  
  150.             <port>${jetty.http.port}</port>  
  151.           </httpConnector>  
  152.           <stopKey>jetty</stopKey>  
  153.           <stopPort>${jetty.stopPort}</stopPort>  
  154.           <!--<scanIntervalSeconds>2</scanIntervalSeconds>-->  
  155.         </configuration>  
  156.       </plugin>  
  157.     </plugins>  
  158.     <finalName>Spring4MVCHelloWorldNoXMLDemo</finalName>  
  159.   </build>  
  160. </project>  

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值