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

来源:互联网 发布:windows激活密匙在哪 编辑:程序博客网 时间:2024/06/06 07:49

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

首先看一下RestController的代码:

package com.dhb.springmvc.controller;import com.dhb.springmvc.entity.User;import com.dhb.springmvc.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;/** * Created by ${denghb} on 2016/7/31. */@RestController@RequestMapping("/springmvc")public class UserController {    @Autowired    private UserService userService;    @RequestMapping(value = "/{name}", method = RequestMethod.GET)    public String sayHello(@PathVariable String name) {        return name;    }    @RequestMapping(value = "/api/addUser", method = RequestMethod.POST)    public int addUserInfo(@RequestBody User user) {        int result = userService.addUser(user);        return result;    }    @RequestMapping(value = "/api/getUser/{id}", method = RequestMethod.GET)    public User getUserInfo(@PathVariable int id) {        return userService.findOneUser(id);    }    @RequestMapping(value = "/api/registerUser", method = RequestMethod.POST)    public Object registerUser(@RequestBody User user) {        return userService.register(user);    }}

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

package com.dhb.springmvc.service;import com.dhb.springmvc.entity.User;import com.dhb.springmvc.repository.UserDao;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;/** * Created by ${denghb} on 2016/8/2. */@Servicepublic class UserService {    @Autowired    UserDao userDao;    public int addUser (User user) {        return userDao.addUser(user);    }    public User findOneUser(int id) {        return userDao.findOneUser(id);    }    public String register(User user) {        User user2 = userDao.findUserByName(user.getName());        if(user2 == null) {            userDao.addUser(user);            return "成功";        } else {            return "失败";        }    }}

下面是repository代码:

package com.dhb.springmvc.repository;import com.dhb.springmvc.entity.User;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;import org.springframework.stereotype.Repository;import javax.sql.DataSource;/** * Created by ${denghb} on 2016/8/2. */@Repositorypublic class UserDao {    private DataSource dataSource;    private JdbcTemplate jdbcTemplate;    @Autowired    public void setDataSource(DataSource dataSource) {        this.dataSource = dataSource;        this.jdbcTemplate = new JdbcTemplate(dataSource);    }    public int addUser(User user) {        String name = user.getName();        String password = user.getPassword();        RowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);        int result = jdbcTemplate.update("insert into user(name, password) values(?,?)",name, password);        return result;    }    public User findOneUser(int id) {        RowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);        User user = jdbcTemplate.queryForObject("select id, name, password from user where id = ?", mapper, id);        return user;    }    public User findUserByName(String name) {        RowMapper<User> mapper = new BeanPropertyRowMapper<>(User.class);        User user = jdbcTemplate.queryForObject("select id, name, password from user where name = ?", mapper, name);        return user;    }}

entity类:

package com.dhb.springmvc.entity;/** * Created by ${denghb} on 2016/8/1. *///@XmlRootElement(name = "demo")//@XmlRootElementpublic class User {    int id;    String name;    String password;    public User() {    }    public User(String name, String password) {        this.name = name;        this.password = password;    }    public User(int id, String name, String password) {        this.id = id;        this.name = name;        this.password = password;    }    //@XmlElement    public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    //@XmlElement    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    //@XmlElement    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }}

增加一个C3P0配置:

package com.dhb.springmvc.config;import com.mchange.v2.c3p0.ComboPooledDataSource;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.PropertySource;/** * Created by ${denghb} on 2016/8/2. */@Configuration//@PropertySource(value = {"classpath:c3p0.properties"})public class C3P0DataSourceBuilder {    /**     * 配置数据源     * @return     */    @Bean(name = "dataSource")    public ComboPooledDataSource getDataSource() {        try {            ComboPooledDataSource dataSource = new ComboPooledDataSource();            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/demo");            dataSource.setDriverClass("com.mysql.jdbc.Driver");            dataSource.setUser("root");            dataSource.setPassword("123456");            dataSource.setMaxPoolSize(75);            return dataSource;        } catch (Exception e) {            return null;        }    }}

先看对应的RestController测试:

package com.dhb.springmvc.controller;import com.dhb.springmvc.config.DhbWebApplicationInitializer;import com.dhb.springmvc.entity.User;import com.dhb.springmvc.service.UserService;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.mockito.InjectMocks;import org.mockito.Mock;import org.mockito.MockitoAnnotations;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import org.springframework.test.context.web.WebAppConfiguration;import org.springframework.test.web.servlet.MockMvc;import org.springframework.test.web.servlet.setup.MockMvcBuilders;import static org.junit.Assert.assertEquals;import static org.mockito.Mockito.verify;import static org.mockito.Mockito.when;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;/** * Created by ${denghb} on 2016/8/2. */@RunWith(SpringJUnit4ClassRunner.class)@WebAppConfiguration@ContextConfiguration(classes = {DhbWebApplicationInitializer.class})public class UserControllerTest {    private MockMvc mockMvc;    @Mock    private UserService userService;    @InjectMocks    private UserController userController;    @Before    public void setup() {        MockitoAnnotations.initMocks(this);        this.mockMvc = MockMvcBuilders.standaloneSetup(userController).build();    }    @Test    public void testAdd() {        int id = 1;        String name = "邓海波";        String password = "123456";        User user = new User();        user.setId(id);        user.setName(name);        user.setPassword(password);        when(userService.addUser(user)).thenReturn(1);        int restUser = userController.addUserInfo(user);        assertEquals(1, restUser);        verify(userService).addUser(user);    }    @Test    public void testGetUserInfo() throws Exception {        int userId = 1;        String userName = "邓海波";        String userPassword = "123456";        User user = new User();        user.setId(userId);        user.setName(userName);        user.setPassword(userPassword);        when(userService.findOneUser(userId)).thenReturn(user);        mockMvc.perform(get("/springmvc/api/getUser/{id}", userId))                .andDo(print())                .andExpect(status().isOk())                .andExpect(jsonPath("id").value(userId))                .andExpect(jsonPath("name").value(userName))                .andExpect(jsonPath("password").value(userPassword));        verify(userService).findOneUser(userId);    }}

首先是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打印处理的信息类似下面显示的内容:

MockHttpServletRequest:         HTTP Method = GET         Request URI = /springmvc/api/getUser/1          Parameters = {}             Headers = {}             Handler:                Type = com.dhb.springmvc.controller.UserController              Method = public com.dhb.springmvc.entity.User com.dhb.springmvc.controller.UserController.getUserInfo(int)               Async:   Was async started = false        Async result = null  Resolved Exception:                Type = null        ModelAndView:           View name = null                View = null               Model = null            FlashMap:MockHttpServletResponse:              Status = 200       Error message = null             Headers = {Content-Type=[application/json;charset=UTF-8]}        Content type = application/json;charset=UTF-8                Body = {"id":1,"name":"邓海波","password":"123456"}       Forwarded URL = null      Redirected URL = null             Cookies = []

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

package com.dhb.springmvc.service;import com.dhb.springmvc.config.DhbWebApplicationInitializer;import com.dhb.springmvc.entity.User;import com.dhb.springmvc.repository.UserDao;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.mockito.InjectMocks;import org.mockito.Mock;import org.mockito.MockitoAnnotations;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import static org.hamcrest.Matchers.is;import static org.junit.Assert.assertThat;import static org.mockito.Matchers.any;import static org.mockito.Matchers.anyString;import static org.mockito.Mockito.verify;import static org.mockito.Mockito.when;/** * Created by ${denghb} on 2016/8/3. */@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = {DhbWebApplicationInitializer.class})public class UserServiceTest {    @Mock    private UserDao userDao;    @InjectMocks    private UserService userService;    @Before    public void setup() {        MockitoAnnotations.initMocks(this);    }    @Test    public void testRegister() {        when(userDao.findUserByName(anyString())).thenReturn(null);        when(userDao.addUser(any(User.class))).thenReturn(0);        assertThat(userService.register(new User("邓海波", "567890")), is("成功"));        verify(userDao).findUserByName(anyString());        verify(userDao).addUser(any(User.class));    }}

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

这是pom.xml文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  <modelVersion>4.0.0</modelVersion>  <groupId>com.dhb.demo</groupId>  <artifactId>spring4MVCHelloWorldNoXMLDemo</artifactId>  <packaging>war</packaging>  <version>0.1.0-SNAPSHOT</version>  <name>spring4MVCHelloWorldNoXMLDemo Maven Webapp</name>  <url>http://maven.apache.org</url>  <properties>    <jetty.context>/</jetty.context>    <jetty.http.port>9089</jetty.http.port>    <jetty.https.port>9444</jetty.https.port>    <jetty.stopPort>10081</jetty.stopPort>    <spring.version>4.1.4.RELEASE</spring.version>  </properties>  <dependencies>    <!-- spring -->    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-web</artifactId>      <version>${spring.version}</version>    </dependency>    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-webmvc</artifactId>      <version>${spring.version}</version>    </dependency>    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-expression</artifactId>      <version>${spring.version}</version>    </dependency>    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-context-support</artifactId>      <version>${spring.version}</version>    </dependency>    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-test</artifactId>      <version>${spring.version}</version>    </dependency>    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-jdbc</artifactId>      <version>${spring.version}</version>    </dependency>    <!-- junit -->    <dependency>      <groupId>junit</groupId>      <artifactId>junit</artifactId>      <version>4.12</version>      <scope>test</scope>    </dependency>    <!-- jackson -->    <dependency>      <groupId>org.codehaus.jackson</groupId>      <artifactId>jackson-mapper-asl</artifactId>      <version>1.9.13</version>    </dependency>    <dependency>      <groupId>com.fasterxml.jackson.core</groupId>      <artifactId>jackson-databind</artifactId>      <version>2.3.4</version>    </dependency>    <dependency>      <groupId>javax.servlet</groupId>      <artifactId>javax.servlet-api</artifactId>      <version>3.1.0</version>    </dependency>    <dependency>      <groupId>javax.servlet.jsp</groupId>      <artifactId>javax.servlet.jsp-api</artifactId>      <version>2.3.1</version>    </dependency>    <dependency>      <groupId>javax.servlet</groupId>      <artifactId>jstl</artifactId>      <version>1.2</version>    </dependency>    <!-- mockito -->    <dependency>      <groupId>org.mockito</groupId>      <artifactId>mockito-core</artifactId>      <version>1.10.19</version>      <scope>test</scope>    </dependency>    <!-- jsonPath -->    <dependency>      <groupId>com.jayway.jsonpath</groupId>      <artifactId>json-path</artifactId>      <version>2.2.0</version>    </dependency>    <dependency>      <groupId>com.jayway.jsonpath</groupId>      <artifactId>json-path-assert</artifactId>      <version>2.2.0</version>    </dependency>    <!--c3po-->    <dependency>      <groupId>c3p0</groupId>      <artifactId>c3p0</artifactId>      <version>0.9.1.2</version>    </dependency>    <dependency>      <groupId>mysql</groupId>      <artifactId>mysql-connector-java</artifactId>      <version>5.1.6</version>    </dependency>  </dependencies>  <build>    <plugins>      <plugin>        <groupId>org.apache.maven.plugins</groupId>        <artifactId>maven-compiler-plugin</artifactId>        <version>3.1</version>        <configuration>          <source>1.7</source>          <target>1.7</target>          <showWarnings>true</showWarnings>        </configuration>      </plugin>      <!--maven jetty 插件配置-->      <plugin>        <groupId>org.eclipse.jetty</groupId>        <artifactId>jetty-maven-plugin</artifactId>        <version>9.2.17.v20160517</version>        <configuration>          <webApp>            <contextPath>${jetty.context}</contextPath>          </webApp>          <httpConnector>            <port>${jetty.http.port}</port>          </httpConnector>          <stopKey>jetty</stopKey>          <stopPort>${jetty.stopPort}</stopPort>          <!--<scanIntervalSeconds>2</scanIntervalSeconds>-->        </configuration>      </plugin>    </plugins>    <finalName>Spring4MVCHelloWorldNoXMLDemo</finalName>  </build></project>


这里介绍省略了一些spring web环境的配置,可以去我前面几篇博客去看哈~


1 1