Spring整合Spring MVC及Mybatis进行Junit单元测试

我们可以在不启动服务的情况下,进行单元测试,以便提交出高质量的代码。本文以一个小例子,说明在Spring中如何进行单元测试。

一:测试Controller

1:在pom.xml文件中引入相关依赖

  <properties>
    <!-- 设置项目编码编码 -->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <!-- spring版本号 -->
    <spring.version>4.3.5.RELEASE</spring.version>
    <!-- mybatis版本号 -->
    <mybatis.version>3.4.1</mybatis.version>
  </properties>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12-beta-3</version>
    </dependency>

   <!--引入文件上传-->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.1</version>
      <exclusions>
        <exclusion>
          <artifactId>commons-io</artifactId>
          <groupId>commons-io</groupId>
        </exclusion>
      </exclusions>
    </dependency>

2:编写被测试类--UserController

import com.qiqi.juint.test.model.vo.UserVO;
import com.qiqi.juint.test.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "getUser",method = RequestMethod.GET)
    @ResponseBody
    public List<UserVO> getUserInfo(@RequestParam Integer age){
        return userService.getUserInfo(age);
    }
}

3:测试类--UserControllerTest

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
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.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

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.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;


@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration //声明一个ApplicationContext集成测试加载WebApplicationContext,作用是模拟ServletContext
@ContextConfiguration(locations={"classpath:spring/spring-application.xml"})
public class UserControllerTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup()  {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    @Test
    public void testGetUserInfo() throws Exception {

        MvcResult mvResult = mockMvc.perform(MockMvcRequestBuilders.get("/user/getUser").param("age","12"))
                .andDo(print())
                .andReturn();//最后返回相应的MvcResult

    }
}

测试结果:

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /user/getUser
       Parameters = {age=[12]}
          Headers = {}

Handler:
             Type = com.qiqi.juint.test.controller.UserController
           Method = public java.util.List<com.qiqi.juint.test.model.vo.UserVO> com.qiqi.juint.test.controller.UserController.getUserInfo(java.lang.Integer)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = {Content-Type=[application/json;charset=UTF-8]}
     Content type = application/json;charset=UTF-8
             Body = [{"id":2,"age":12,"name":"小花"},{"id":3,"age":12,"name":"小兰"},{"id":7,"age":12,"name":"大师兄"}]
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

二:测试Service

引入的依赖和测试Controller相同。测试时,在Service类上添加相应的注解就可以进行测试,不用单独写测试类。

import com.qiqi.juint.test.dao.UserMapper;
import com.qiqi.juint.test.model.vo.UserVO;
import com.qiqi.juint.test.service.UserService;
import org.apache.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.transaction.Transactional;
import java.util.List;

/**
 * Created by ZhaoQiqi on 2018/11/8.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/spring-application.xml"})
@Transactional
@Service
public class UserServiceImpl implements UserService {

    private static final Logger logger = Logger.getLogger(UserServiceImpl.class);

    @Autowired
    private UserMapper userMapper;

    public List<UserVO> getUserInfo(Integer age) {

        logger.info("调用方法getUserInfo(Integer age)");
        return userMapper.getUserInfo(age);
    }

    @Test
    public void test(){
        List<UserVO> list = userMapper.getUserInfo(12);
        System.out.println(list);
    }
}

测试结果:

13:20:09.492 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
13:20:09.496 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@50305a]
13:20:09.503 [main] DEBUG o.m.s.t.SpringManagedTransaction - JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@6b9267b [wrapping: com.mysql.jdbc.JDBC4Connection@408b35bf]] will be managed by Spring
13:20:09.506 [main] DEBUG c.q.j.t.dao.UserMapper.getUserInfo - ==>  Preparing: SELECT `id` as id, `age` as age, `name` as name FROM user WHERE age = ?; 
13:20:09.525 [main] DEBUG c.q.j.t.dao.UserMapper.getUserInfo - ==> Parameters: 12(Integer)
13:20:09.541 [main] DEBUG c.q.j.t.dao.UserMapper.getUserInfo - <==      Total: 3
13:20:09.546 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@50305a]
[id:2  age:12  name:小花, id:3  age:12  name:小兰, id:7  age:12  name:大师兄]
13:20:09.546 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@50305a]
13:20:09.546 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@50305a]

 

三:在测试类中添加回滚注解,避免测试数据污染数据库

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration //声明一个ApplicationContext集成测试加载WebApplicationContext,作用是模拟ServletContext
@ContextConfiguration(locations={"classpath:spring/spring-application.xml"})

//配置事务的回滚,对数据库的增删改都会回滚,便于测试用例的循环利用
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@Transactional
public class UserControllerTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup()  {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    @Test
    public void testGetUserInfo() throws Exception {

        MvcResult mvResult = mockMvc.perform(MockMvcRequestBuilders.get("/user/getUser")
                .param("age","12"))
                .andDo(print())
                .andReturn();//最后返回相应的MvcResult

    }

    @Test
    public void testInsertUser() throws Exception {
        User user = new User(15,"哈哈");
        String requestJson = JSONObject.toJSONString(user);
        System.out.println(requestJson);
        MvcResult mvResult = mockMvc.perform(
                MockMvcRequestBuilders.post("/user/insertUser")
                        .contentType(MediaType.APPLICATION_JSON).content(requestJson))
                .andDo(print())
                .andReturn();//最后返回相应的MvcResult
    }
}

这样在测试时,对数据库的增删改都会回滚,数据库数据不会改变。在非测试时,即正常调用时,若被测试类没有进行回滚注解,数据库数据自然会改变。

四、相关代码的解释:

@webappconfiguration是一级注释,用于声明一个ApplicationContext集成测试加载WebApplicationContext。作用是模拟ServletContext。

@ContextConfiguration:因为controller,component等都是使用注解,需要注解指定spring的配置文件,扫描相应的配置,将类初始化等。

@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)

@Transactional

上面两句的作用是,让我们对数据库的操作会事务回滚,如对数据库的添加操作,在方法结束之后,会撤销我们对数据库的操作。

为什么要事务回滚?

测试过程对数据库的操作,会产生脏数据,影响我们数据的正确性

不方便循环测试,即假如这次我们将一个记录删除了,下次就无法再进行这个Junit测试了,因为该记录已经删除,将会报错。

如果不使用事务回滚,我们需要在代码中显式的对我们的增删改数据库操作进行恢复,将多很多和测试无关的代码 

方法解析:

perform:执行一个RequestBuilder请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理;

get:声明发送一个get请求的方法。MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables):根据uri模板和uri变量值得到一个GET请求方式的。另外提供了其他的请求的方法,如:post、put、delete等。

param:添加request的参数,如上面发送请求的时候带上了了pcode = root的参数。假如使用需要发送json数据格式的时将不能使用这种方式,可见后面被@ResponseBody注解参数的解决方法

andExpect:添加ResultMatcher验证规则,验证控制器执行完成后结果是否正确(对返回的数据进行的判断);

andDo:添加ResultHandler结果处理器,比如调试时打印结果到控制台(对返回的数据进行的判断);

andReturn:最后返回相应的MvcResult;然后进行自定义验证/进行下一步的异步处理(对返回的数据进行的判断);

猜你喜欢

转载自blog.csdn.net/qq_27127145/article/details/83858035