java springmvc 利用junit4和mockMvc对controller层进行单元测试

8 篇文章 0 订阅
4 篇文章 0 订阅

利用junit4和mockMvc进行springMVC的controller的接口单元测试

一、概述

自从用了springMVC后,对于service层和dao层的测试,已经很熟悉了,但是对于controller层,要知道controller层的逻辑是否正确,就必须启动服务,但是这样稍微有点改动,就必须启动服务,十分麻烦。还好,spring提供了mockMvc模块,可以模拟web请求来对controller层进行单元测试

二、正文
1、准备工作

首先,要准备一个简单工程,我这里用的ssm来搭建的工程框架,并且使用了mybaits的通用mapper和分页插件pagehelper。这里不进行赘述,以后会单独写一篇来详细描述搭建ssm框架过程,或者读者可以直接百度,百度上还是有很多资源的。下面直接上源码。
代码结构:
这里写图片描述
注意,我这里把entity层和service和一些基础类,提取到另一个工程中了,和放在同一个工程里是一样的。
配置文件
spring-mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans    
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd   
                        http://www.springframework.org/schema/tx 
                        http://www.springframework.org/schema/tx/spring-tx-4.0.xsd 
                        http://www.springframework.org/schema/context    
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd    
                        http://www.springframework.org/schema/mvc    
                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
    <!-- 自动扫描包 -->
    <context:component-scan base-package="com.charlotte.blog" />

    <!-- 引入配置文件 -->
    <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:spring/app.properties" />
    </bean>

    <!-- 阿里 druid 数据库连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close">
        <!-- 数据库基本信息配置 -->
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
        <property name="driverClassName" value="${driverClassName}" />
        <property name="filters" value="${filters}" />
        <!-- 最大并发连接数 -->
        <property name="maxActive" value="${maxActive}" />
        <!-- 初始化连接数量 -->
        <property name="initialSize" value="${initialSize}" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="${maxWait}" />
        <!-- 最小空闲连接数 -->
        <property name="minIdle" value="${minIdle}" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
        <property name="validationQuery" value="${validationQuery}" />
        <property name="testWhileIdle" value="${testWhileIdle}" />
        <property name="testOnBorrow" value="${testOnBorrow}" />
        <property name="testOnReturn" value="${testOnReturn}" />
        <property name="maxOpenPreparedStatements" value="${maxOpenPreparedStatements}" />
        <!-- 打开 removeAbandoned 功能 -->
        <property name="removeAbandoned" value="${removeAbandoned}" />
        <!-- 1800 秒,也就是 30 分钟 -->
        <property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
        <!-- 关闭 abanded 连接时输出错误日志 -->
        <property name="logAbandoned" value="${logAbandoned}" />
    </bean>

    <!-- spring整合mybatis -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 自动扫描mapping.xml文件 -->
        <property name="mapperLocations" value="classpath:com/charlotte/blog/mapper/*.xml" />
        <!-- 设置pageHelper -->
        <property name="configLocation" value="classpath:mybatis-config.xml" />
    </bean>

    <!-- DAO接口所在包名,Spring会自动查找其下的类 -->
    <!-- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property 
        name="basePackage" value="com.charlotte.blog.mapper" /> <property name="sqlSessionFactoryBeanName" 
        value="sqlSessionFactory" /> </bean> -->

    <!-- mybatis mapper scan -->
    <bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.charlotte.blog.mapper" />
        <property name="markerInterface" value="tk.mybatis.mapper.common.Mapper" />
        <property name="properties">
            <value>
                IDENTITY=MYSQL
                ORDER=AFTER
                notEmpty=FLASE
            </value>
        </property>
    </bean>

    <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"
        proxy-target-class="true" />
</beans>

spring-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:mvc="http://www.springframework.org/schema/mvc"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans    
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd    
                        http://www.springframework.org/schema/context    
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd    
                        http://www.springframework.org/schema/mvc    
                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

    <!-- 自动扫描该包,使SpringMVC认为包下用了@controller注解的类是控制器 -->  
    <context:component-scan base-package="com.charlotte.blog.controller" />

    <mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean class="com.charlotte.sdk.common.beans.MyFastJsonHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json</value>
                    </list>
                </property>
                <property name="features">
                    <array value-type="com.alibaba.fastjson.serializer.SerializerFeature">
                        <value>DisableCircularReferenceDetect</value>
                        <!--<value>UseISO8601DateFormat</value>-->
                        <value>WriteMapNullValue</value>
                        <value>WriteNullListAsEmpty</value>
                        <value>WriteNullStringAsEmpty</value>
                        <value>WriteNullBooleanAsFalse</value>
                    </array>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

</beans>

spring-db.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"
    default-lazy-init="true">

    <!-- 读入属性文件 -->
    <context:property-placeholder location="classpath:spring/app.properties" />

    <!-- 阿里 druid 数据库连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close">
        <!-- 数据库基本信息配置 -->
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
        <property name="driverClassName" value="${driverClassName}" />
        <property name="filters" value="${filters}" />
        <!-- 最大并发连接数 -->
        <property name="maxActive" value="${maxActive}" />
        <!-- 初始化连接数量 -->
        <property name="initialSize" value="${initialSize}" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="${maxWait}" />
        <!-- 最小空闲连接数 -->
        <property name="minIdle" value="${minIdle}" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
        <property name="validationQuery" value="${validationQuery}" />
        <property name="testWhileIdle" value="${testWhileIdle}" />
        <property name="testOnBorrow" value="${testOnBorrow}" />
        <property name="testOnReturn" value="${testOnReturn}" />
        <property name="maxOpenPreparedStatements" value="${maxOpenPreparedStatements}" />
        <!-- 打开 removeAbandoned 功能 -->
        <property name="removeAbandoned" value="${removeAbandoned}" />
        <!-- 1800 秒,也就是 30 分钟 -->
        <property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
        <!-- 关闭 abanded 连接时输出错误日志 -->
        <property name="logAbandoned" value="${logAbandoned}" />
    </bean>

    <!-- spring整合mybatis -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 自动扫描mapping.xml文件 -->
        <property name="mapperLocations" value="classpath:com/charlotte/blog/mapper/*.xml" />
        <!-- 设置pageHelper -->
        <property name="configLocation" value="mybatis-config.xml" />
    </bean>
</beans>

属性文件app.properties

# 数据库连接配置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/blog
username=root
password=123456
filters: stat  
maxActive: 20  
initialSize: 1  
maxWait: 60000  
minIdle: 10  
maxIdle: 15  
timeBetweenEvictionRunsMillis: 60000  
minEvictableIdleTimeMillis: 300000  
validationQuery: SELECT 'x'  
testWhileIdle: true  
testOnBorrow: false  
testOnReturn: false  
maxOpenPreparedStatements: 20  
removeAbandoned: true  
removeAbandonedTimeout: 1800  
logAbandoned: true  

#redis 配置
redis.host=127.0.0.1
redis.port=6379 
redis.pass=
redis.dbIndex=0 
redis.expiration=3000  
redis.maxIdle=300 
redis.maxActive=600 
redis.maxWait=1000 
redis.testOnBorrow=true

controller层,以AccountInfoController为例

/**
 * 
 */
package com.charlotte.blog.controller.account;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.charlotte.blog.controller.BaseController;
import com.charlotte.sdk.common.beans.BaseResponse;
import com.charlotte.sdk.common.utils.page.PageHandler;
import com.charlotte.sdk.main.entity.BlogAccountInfo;
import com.charlotte.sdk.main.form.account.AccountInfoSaveForm;
import com.charlotte.sdk.main.form.account.AccountInfoSearchForm;
import com.charlotte.sdk.main.service.account.AccountInfoService;
import com.github.pagehelper.PageInfo;

/**
 * @author dingjunjie
 *
 */
@Controller
@RequestMapping(value = "/account")
public class AccountInfoController extends BaseController{

    @Autowired
    private AccountInfoService accountInfoService;

    @ResponseBody
    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public BaseResponse test() {
        BaseResponse response = new BaseResponse();
        AccountInfoSearchForm searchForm = new AccountInfoSearchForm();
        PageInfo<BlogAccountInfo> result = accountInfoService.search(searchForm, new PageHandler());
        response.setData(result);
        return response;
    }

    @ResponseBody
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public BaseResponse add(@Validated AccountInfoSaveForm saveForm) {
        saveForm.setUserName("admin");
        saveForm.setPasswordSalt("test");
        saveForm.setUserPassword("123456");
        BaseResponse response = new BaseResponse();
        accountInfoService.saveByForm(saveForm, null);
        return response;
    }

}
2、创建测试基类

首先看下测试包的代码结构
这里写图片描述
这里最主要的就是BaseTest.java,代码如下

/**
 * 
 */
package com.charlotte.blog.base;

import org.junit.Before;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
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 org.springframework.web.context.WebApplicationContext;

/**
 * 单元测试基础类
 * 
 * @author dingjunjie
 * @date 2018-01-28 17:02:30
 */
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration(value = "src/main/webapp")
@ContextConfiguration(locations={"classpath:spring/spring-mybatis.xml","classpath:spring/spring-mvc.xml"})
@ComponentScan(basePackages={"com.charlotte.blog.controller", "com.charlotte.sdk.main.service"})
//当然 你可以声明一个事务管理 每个单元测试都进行事务回滚 无论成功与否
//@TransactionConfiguration( transactionManager = "transactionManager",defaultRollback = true)
//@Transactional
public class BaseTest {

    public Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private WebApplicationContext webApplicationContext;

    protected MockMvc mockMvc;

    protected MockHttpServletRequest request;
    protected MockHttpServletResponse response;

    /**
     * 初始化SpringmvcController类测试环境
     */
    @Before
    public void setup(){
        //加载web容器上下文
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

}

我把这个类写成了基类,以后要新增单元测试,只要继承该类,就行了。
例如测试accountInfoController的方法,可以如下这样写

/**
 * 
 */
package com.charlotte.blog.controller.account;

import org.junit.Test;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import com.charlotte.blog.base.BaseTest;

/**
 * @author dingjunjie
 *
 */
public class TestAccountController extends BaseTest{

    @Test
    public void testTest(){
        try {
            ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get("/account/test"));
            MvcResult mvcResult = resultActions.andDo(MockMvcResultHandlers.print()).andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
            String result = mvcResult.getResponse().getContentAsString();
            System.out.println("==========结果为:==========\n" + result + "\n");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testAdd(){
        try {
            ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get("/account/add"));
            MvcResult mvcResult = resultActions.andReturn();
            String result = mvcResult.getResponse().getContentAsString();
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这里,其实还能够优化,可以把

ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get("/account/test"));
            MvcResult mvcResult = resultActions.andDo(MockMvcResultHandlers.print()).andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
            String result = mvcResult.getResponse().getContentAsString();

这部分代码封装成一个方法,放到BaseTest.java中去,访问url和参数paramMap作为参数,这样,继承类中,只要直接调用该方法,就能获得数据,更加方便。后续如果优化了,会直接更新到该文中,读者也可自行编写,还是比较容易的。

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值