SpringBoot基础之MockMvc单元测试

什么是Mock

在面向对象的程序设计中,模拟对象(英语:mock object)是以可控的方式模拟真实对象行为的假对象。在编程过程中,通常通过模拟一些输入数据,来验证程序是否达到预期结果。

为什么使用Mock对象

使用模拟对象,可以模拟复杂的、真实的对象行为。如果在单元测试中无法使用真实对象,可采用模拟对象进行替代。
spring测试框架提供了两种方式,独立安装和集成Web环境测试(此种方式并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)

MockMvc

MockMvc是由spring-test包提供,实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,使得测试速度快、不依赖网络环境。同时提供了一套验证的工具,结果的验证十分方便。

接口MockMvcBuilder,提供一个唯一的build方法,用来构造MockMvc。主要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder,分别对应两种测试方式,即独立安装和集成Web环境测试(并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)。MockMvcBuilders提供了对应的创建方法standaloneSetup方法和webAppContextSetup方法,在使用时直接调用即可。
1、mockMvc.perform执行一个请求;
2、MockMvcRequestBuilders.get("/user/1")构造一个请求
3、ResultActions.andExpect添加执行完成后的断言
4、ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
5、ResultActions.andReturn表示执行完成后返回相应的结果。
MockMvcBuilder是用来构造MockMvc的构造器,其主要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder,StandaloneMockMvcBuilder继承了DefaultMockMvcBuilder。直接使用静态工厂MockMvcBuilders创建即可:
MockMvcBuilders.webAppContextSetup(WebApplicationContext context):指定WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc;

方法注解
@Test
    // 执行测试要执行的数据库脚本(脚本位置: src/test/resources/sql/DemoControllerTest/testGetOne.sql)
    @Sql(scripts = {"classpath:sql/DemoControllerTest/testGetOne.sql"})
    // 事务回滚,方法执行完后,把刚开始执行脚本的数据恢复
    @Rollback(true)
    // 开始事务才会回滚,不然每次执行数据库脚本插入重复数据
    @Transactional 
public void testGetOne() throws Exception {
}
使用方法

整个过程如下:
1、准备测试环境
2、通过MockMvc执行请求
3、添加验证断言
4、添加结果处理器
5、得到MvcResult进行自定义断言/进行下一步的异步请求


import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSONObject;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import javax.servlet.Filter;
import javax.ws.rs.core.MediaType;

import java.util.ArrayList;
import java.util.List;

import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = JPaasBpmApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc // 测试接口用
public class BpmDefControllerTest {


    @Autowired
    private MockMvc mockMvc;
    @Autowired
    private WebApplicationContext context;
    @Autowired
    private FilterRegistrationBean registerMessageFilter;
    @Autowired
    private FilterRegistrationBean registerTraceFilter;
    @Autowired
    private FilterRegistrationBean registerAppIdFilter;
    @Autowired
    private FilterRegistrationBean registerWebFilter;


    HttpHeaders httpHeaders;


    private String userStr;
//加载nacos配置
    @BeforeClass
    public static void setup() throws Exception {
        System.setProperty("nacos-server-addr", "172.16.255.254:8848");
        System.setProperty("nacosnacos-namespace", "public");
    }

    @Before
    public void setupMockMvc() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(context).addFilters(registerMessageFilter.getFilter(), registerTraceFilter.getFilter(), registerAppIdFilter.getFilter(), registerWebFilter.getFilter()).build();

    }

    @Before
    public void setUserHeader() throws Exception {
        httpHeaders = new HttpHeaders();
        JPaasUser paasUser = new JPaasUser();
        paasUser.setAccount("000616");
        paasUser.setUserId("81ae33459f684679b89b2419114df881");
        paasUser.setTenantId("7c331c3059bb4b48831454894afe2354");
        paasUser.setFullName("彭静");
        String jpaasUser = JSONObject.toJSONString(paasUser);
        userStr = Base64.encode(jpaasUser);
        httpHeaders.add("jpaas-user", userStr);
        httpHeaders.add(SecurityConstants.Authorization, "Bearer bcf186b8f3764f6ea01c374b36ba5e96");
    }


    /**
     * 获取主流程对应的子流程(调用工单接口,只需要返回200即可)
     *
     * @throws Exception
     */
    @Test
    public void getCandidateSubPrecess() throws Exception {
        String requestBody = "{\n" +
                "    \"mainProcessAlis\":\"Process_4736482344436\"" +
                "}";
        String responseString = mockMvc.perform(MockMvcRequestBuilders.get("/Precess")
                .headers(httpHeaders)
                .content(requestBody)
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON)) //执行请求
                //.andExpect(content().contentType(MediaType.APPLICATION_JSON)) //验证响应contentType
                .andDo(print())         //打印出请求和相应的内容
                .andReturn().getResponse().getContentAsString();   //将相应的数据转换为字符串;
        System.out.println("获取结果为:" + responseString);
        //
    }
断言条件
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.code").value(200))
.andExpect(MockMvcResultMatchers.jsonPath("$.data.type").value("GOOD"))
自定义断言
Assert.assertNotNull(result.getModelAndView().getModel().get("user")); //自定义断言

请求方法

MockMvcRequestBuilders主要API:

MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables):根据uri模板和uri变量值得到一个GET请求方式的MockHttpServletRequestBuilder;如get("/user/{id}", 1L)MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables):同get类似,但是是POST方法;
MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables):同get类似,但是是PUT方法;
MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) :同get类似,但是是DELETE方法;
MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables):同get类似,但是是OPTIONS方法;

String responseString = mockMvc.perform(MockMvcRequestBuilders.get("/drecess")
String responseString = mockMvc.perform(MockMvcRequestBuilders.post("/brecess")

传参方式

Json参数:
String requestBody = "{\n" +
        "    \"pageNo\": 1,\n" +
        "    \"pageSize\": 10,\n" +
        "    \"sortField\": \"\",\n" +
        "    \"sortOrder\": \"asc\",\n" +
        "    \"params\": {\n" +
        "        \"Q_STATUS__S_EQ\": \"DEPLOYED\"\n" +
        "    }\n" +
        "}";
String responseString = mockMvc.perform(MockMvcRequestBuilders.post("/bpm/core/bpmDef/query")
        .headers(httpHeaders)
        .content(requestBody)
        //.param("boAlias", "ban_reason")
        .contentType(MediaType.APPLICATION_JSON)
Form data参数
.param("key", "Process_4736482344436")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
主要方法:

perform:执行一个RequestBuilder请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理;
  andExpect:添加ResultMatcher验证规则,验证控制器执行完成后结果是否正确;
  andDo:添加ResultHandler结果处理器,比如调试时打印结果到控制台;
  andReturn:最后返回相应的MvcResult;然后进行自定义验证/进行下一步的异步处理

未使用的方法
StandaloneMockMvcBuilder继承了DefaultMockMvcBuilder,又提供了如下API:
setMessageConverters(HttpMessageConverter<?>...messageConverters):设置HTTP消息转换器;
setValidator(Validator validator):设置验证器;
setConversionService(FormattingConversionService conversionService):设置转换服务;
addInterceptors(HandlerInterceptor... interceptors)/addMappedInterceptors(String[] pathPatterns, HandlerInterceptor... interceptors):添加spring mvc拦截器;
setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager):设置内容协商管理器;
setAsyncRequestTimeout(long timeout):设置异步超时时间;
setCustomArgumentResolvers(HandlerMethodArgumentResolver... argumentResolvers):设置自定义控制器方法参数解析器;
setCustomReturnValueHandlers(HandlerMethodReturnValueHandler... handlers):设置自定义控制器方法返回值处理器;
setHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers)/setHandlerExceptionResolvers(HandlerExceptionResolver... exceptionResolvers):设置异常解析器;
setViewResolvers(ViewResolver...resolvers):设置视图解析器;
setSingleView(View view):设置单个视图,即视图解析时总是解析到这一个(仅适用于只有一个视图的情况);
setLocaleResolver(LocaleResolver localeResolver):设置Local解析器;
setFlashMapManager(FlashMapManager flashMapManager):设置FlashMapManager,如存储重定向数据;
setUseSuffixPatternMatch(boolean useSuffixPatternMatch):设置是否是后缀模式匹配,如“/user”是否匹配"/user.*",默认真即匹配;
setUseTrailingSlashPatternMatch(boolean useTrailingSlashPatternMatch):设置是否自动后缀路径模式匹配,如“/user”是否匹配“/user/”,默认真即匹配;
addPlaceHolderValue(String name, String value) :添加request mapping中的占位符替代;
  • 7
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值