SpringBoot的测试环境搭建和讲解(简单易懂)

SpringBoot的测试环境搭建

前言

这里来讲讲我个人进行SprigBoot测试环境搭建的学习和踩坑,方便我们单间个人测试案例,对关键的接口进行测试保障。减少后期的运维成本。


搭建基本环境

Jar包的引入

返回目录

  • SpringBoot的核心依赖
  • Json工具,这里我还是使用简单的Hutools
<!-- 核心Web包 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.8</version>
</dependency>
<!-- 测试包 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

建立我们的测试接口

返回目录

主要用于测试

package com.example.basic.controller;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.basic.entity.DictBed;
import com.example.basic.service.IDictBedService;
import com.example.basic.utils.resultbean.ErrorCodeInfo;
import com.example.basic.utils.resultbean.ResultBean;
import io.swagger.annotations.ApiOperation;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import java.util.List;

/**
 * @author YeZhiyue
 * Description 床类型字典表 服务实现类
 * Date 2020/10/01
 * Time 16:35
 * Mail 739153436@qq.com
 */
@Component
@RestController
@RequestMapping("/dictBed")
public class DictBedController {

        @Resource
        private IDictBedService iDictBedService;

        /**
         * @description 新增
         * @author YeZhiyue
         * @email 739153436@qq.com
         * @date 2020/10/01 16:35
         */
        @ApiOperation("新增")
        @PutMapping("/admin/insert")
        public ResultBean<Boolean> adminInsert(@RequestBody List<DictBed> pDictBedList) {
                return ResultBean.restResult(iDictBedService.adminInsert(pDictBedList), ErrorCodeInfo.CREATED);
        }

        /**
         * @description 删除
         * @author YeZhiyue
         * @email 739153436@qq.com
         * @date 2020/10/01 16:35
         */
        @ApiOperation("删除")
        @DeleteMapping("/admin/delete")
        public ResultBean<Boolean> adminDelete(@NotEmpty(message = "删除的id列表不能为空") @RequestBody List<String> pIdList) {
                return ResultBean.restResult(iDictBedService.adminDelete(pIdList), ErrorCodeInfo.NO_CONTENT);
        }

        /**
         * @description 分页查询
         * @author YeZhiyue
         * @email 739153436@qq.com
         * @date 2020/10/01 16:35
         */
        @ApiOperation("分页查询")
        @GetMapping("/admin/page")
        public ResultBean<Page<DictBed>> adminPage(@Min(value = -1, message = "页码过小") int pPageNum, int pPageSize, @RequestBody DictBed pDictBed) {
                return ResultBean.restResult(iDictBedService.adminPage(pPageNum, pPageSize, pDictBed), ErrorCodeInfo.OK);
        }

        /**
         * @description 列表查询
         * @author YeZhiyue
         * @email 739153436@qq.com
         * @date 2020/10/01 16:35
         */
        @ApiOperation("分页查询")
        @GetMapping("/admin/list")
        public ResultBean<List<DictBed>> adminList() {
                return ResultBean.restResult(iDictBedService.list(), ErrorCodeInfo.OK);
        }

        /**
         * @description 更新
         * @author YeZhiyue
         * @email 739153436@qq.com
         * @date 2020/10/01 16:35
         */
        @ApiOperation("更新")
        @PostMapping("/admin/update")
        public ResultBean<Boolean> adminUpdate(@Validated(value = DictBed.Update.class) @RequestBody List<DictBed> pDictBedList) {
                return ResultBean.restResult(iDictBedService.adminUpdate(pDictBedList), ErrorCodeInfo.OK);
        }
}

建立我们的测试类

MockMVC 介绍

返回目录

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

接口MockMvcBuilder,提供一个唯一的build方法,用来构造MockMvc。主要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder,分别对应两种测试方式,即独立安装和集成Web环境测试(并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)。MockMvcBuilders提供了对应的创建方法standaloneSetup方法和webAppContextSetup方法,在使用时直接调用即可。

参考博客,下面这篇博客可以参考一下

SpringBoot基础之MockMvc单元测试

使用示例

返回目录

简单示例测试类

import cn.hutool.json.JSONUtil;
import com.example.basic.entity.DictBed;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
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.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import java.util.Collections;

/**
 * @author 叶之越
 * Description
 * Date 2020/10/24
 * Time 20:02
 * Mail 739153436@qq.com
 */
@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class MockMvcRestful {

    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext wac;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();  //构建MockMVC
    }


    /**
     * 说明:基本请求操作方法
     * 1、mockMvc.perform执行一个请求。
     * 2、MockMvcRequestBuilders.get("XXX")构造一个请求。
     * 3、ResultActions.param添加请求传值
     * 4、ResultActions.accept(MediaType.TEXT_HTML_VALUE))设置返回类型
     * 5、ResultActions.andExpect添加执行完成后的断言。
     * 6、ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情
     *   比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
     * 7、ResultActions.andReturn表示执行完成后返回相应的结果。
     */
    @Test
    public void insert() throws Exception {
        DictBed build = new DictBed();
        build.setCnName("测试床");
        build.setDescription("测试床描述");
        build.setEnName("TestBed");
        String requestJson = JSONUtil.toJsonStr(Collections.singleton(build));
        String responseString = mockMvc.perform(MockMvcRequestBuilders
                .put("/dictBed/admin/insert")
                // 设置参数是Json类型,也就是 @RequestBody
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestJson)
        )
                // 打印中间信息
                .andDo(MockMvcResultHandlers.print())
                // 异常判断
                .andExpect(MockMvcResultMatchers.status().isOk())
                // 返回值
                .andReturn().getResponse().getContentAsString();
        System.out.println(responseString);
    }

    /**
     * 请求参数既包含 @RequestBody 和 @RequestParam
     */
    @Test
    public void page() throws Exception {
        DictBed build = new DictBed();
        build.setCnName("测试床");
        build.setDescription("测试床描述");
        build.setEnName("TestBed");
        String requestJson = JSONUtil.toJsonStr(build);

        String responseString = mockMvc.perform(MockMvcRequestBuilders
                        .get("/dictBed/admin/page")
                        // 设置参数是Json类型,也就是 @RequestBody
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(requestJson)
                // 参数是普通的RequestParam
                .param("pPageNum","0")
                .param("pPageSize","10")
                .accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
        )
                // 打印中间信息
                .andDo(MockMvcResultHandlers.print())
                // 异常判断
                .andExpect(MockMvcResultMatchers.status().isOk())
                // 返回值
                .andReturn().getResponse().getContentAsString();
        System.out.println(responseString);
    }


    @Test
    public void getUser() throws Exception {
        String responseString = mockMvc.perform(MockMvcRequestBuilders.get("/dictBed/admin/list")).andReturn().getResponse().getContentAsString();
        System.out.println("result : " + responseString);
    }
}

测试一

MockHttpServletRequest:
      HTTP Method = PUT
      Request URI = /dictBed/admin/insert
       Parameters = {}
          Headers = [Content-Type:"application/json", Content-Length:"75"]
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.example.basic.controller.DictBedController
           Method = com.example.basic.controller.DictBedController#adminInsert(List)

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"]
     Content type = application/json
             Body = {"code":201,"msg":"Created","data":true}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
{"code":201,"msg":"Created","data":true}

测试二

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /dictBed/admin/page
       Parameters = {pPageNum=[0], pPageSize=[10]}
          Headers = [Content-Type:"application/json", Accept:"application/json;charset=UTF-8", Content-Length:"73"]
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.example.basic.controller.DictBedController
           Method = com.example.basic.controller.DictBedController#adminPage(int, int, DictBed)

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 = {"code":200,"msg":"OK","data":{"records":[{"id":"1319998035661230082","weight":0,"createTime":1603547030737,"updateTime":0,"createBy":null,"updateBy":null,"version":0,"deleted":0,"extra":"","tenantId":"","cnName":"测试床","enName":"TestBed","description":"测试床描述"},{"id":"1319997802151768065","weight":0,"createTime":1603546975065,"updateTime":0,"createBy":null,"updateBy":null,"version":0,"deleted":0,"extra":"","tenantId":"","cnName":"测试床","enName":"TestBed","description":"测试床描述"},{"id":"1319989345302429698","weight":0,"createTime":1603544958796,"updateTime":0,"createBy":null,"updateBy":null,"version":0,"deleted":0,"extra":"","tenantId":"","cnName":"测试床","enName":"TestBed","description":"测试床描述"}],"total":3,"size":10,"current":1,"orders":[],"optimizeCountSql":true,"hitCount":false,"countId":null,"maxLimit":null,"searchCount":true,"pages":1}}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []


补充(断言、MockMVC其他请求方式)

示例

返回目录

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

/**
 * @author 叶之越
 * Description
 * Date 2020/10/24
 * Time 20:02
 * Mail 739153436@qq.com
 */
@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class MockMvcSupplement {

    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext wac;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();  //构建MockMVC
    }

    /**
     * 断言方法补充
     */
    @Test
    public void assertSupplement() {
//        void assertEquals(boolean expected, boolean actual):检查两个变量或者等式是否平衡
//        void assertTrue(boolean expected, boolean actual):检查条件为真
//        void assertFalse(boolean condition):检查条件为假
//        void assertNotNull(Object object):检查对象不为空
//        void assertNull(Object object):检查对象为空
//        void assertSame(boolean condition):assertSame() 方法检查两个相关对象是否指向同一个对象
//        void assertNotSame(boolean condition):assertNotSame() 方法检查两个相关对象是否不指向同一个对象
//        void assertArrayEquals(expectedArray, resultArray):assertArrayEquals() 方法检查两个数组是否相等
    }

    /**
     * MockMVC 其他测试接口方法示例补充
     */
    @Test
    public void supplement() {
        String jsonStr = "{\"username\":\"Dopa\",\"passwd\":\"ac3af72d9f95161a502fd326865c2f15\",\"status\":\"1\"}";

//		mockMvc.perform(MockMvcRequestBuilders.get("/hello?name={name}","mrbird"));
//		mockMvc.perform(MockMvcRequestBuilders.post("/user/{id}", 1));
//		mockMvc.perform(MockMvcRequestBuilders.fileUpload("/fileupload").file("file", "文件内容".getBytes("utf-8")));
//		mockMvc.perform(MockMvcRequestBuilders.get("/hello").param("message", "hello"));
//		mockMvc.perform(MockMvcRequestBuilders.get("/hobby/save").param("hobby", "sleep", "eat"));

//		MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
//		params.add("name", "mrbird");
//		params.add("hobby", "sleep");
//		params.add("hobby", "eat");
//		mockMvc.perform(MockMvcRequestBuilders.get("/hobby/save").params(params));
//		mockMvc.perform(MockMvcRequestBuilders.get("/index").sessionAttr(name, value));
//		mockMvc.perform(MockMvcRequestBuilders.get("/index").cookie(new Cookie(name, value)));
//		mockMvc.perform(MockMvcRequestBuilders.get("/index").contentType(MediaType.APPLICATION_JSON_UTF8));
//		mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}", 1).accept(MediaType.APPLICATION_JSON));
//		mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}", 1).header(name, values));

//		mockMvc.perform(MockMvcRequestBuilders.get("/index"))
//		.andDo(MockMvcResultHandlers.print());
    }
}

踩坑

坑点一:搭建我们的MockMVC环境

返回目录

刚开始我使用测试的时候一直报java.lang.NullPointerException的错误,于是找了网上的一篇博客,介绍了两种引入MockMVC的方式,第一种方式有点问题就是引入Controller对象的引入方式不正确需要注意一下,链接如下

[Springboot mockMVC 测试错误 java.lang.NullPointerException](Springboot mockMVC 测试错误 java.lang.NullPointerException)

这里直接介绍我比较推荐的MockMVC引入方法,直接集成Web环境,不需要再单独引入Controller,比较方便

@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class WebApplicationTests {

    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext wac;

    @Before
    public void setup(){
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();  //构建MockMVC
    }

    @Test
    public void getUser() throws Exception {
        String responseString = mockMvc.perform(MockMvcRequestBuilders.post("/getUser")).andReturn().getResponse().getContentAsString();
        System.out.println("result : "+responseString);
    }

}

坑点二:引入MockMVC的引入方式错误

返回目录

在坑点一中的博客中的第一种独立安装MockMVC的方法是有问题的,不能直接通过new的方式来传入MockMVC,这样是会报错的

util.NestedServletException: Request processing failed; nested exception is NullPointerException

解决方案我就放在下面的博客链接中,其实就是改变了Controller的注入方式为@Autowire引入

[成功解决util.NestedServletException: Request processing failed; nested exception is NullPointerException](成功解决util.NestedServletException: Request processing failed; nested exception is NullPointerException)

坑点三:我们使用MockMVC进行返回值测试的时候中文会乱码

返回目录

  • 这里我们就需要设置接收返回值的类型的编码,通常我们会设置成Jason类型,但是不要忘记设置字符编码,通常设置为 UTF-8
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值