spring Boot 配合 spock 完成MVC三层单元测试

一、前言

参考文章:

对于 spock 语法和 groovy 的语法,这里就不再多说了,以及对应的 mockitopowerMockito,这里不再过多说明,后面有时间,会把这些文章给补全。

以上项目在 D:\code\exercise\unittest 目录下

  • spock-maven 这个就是本篇文章说的
  • unit-test 这个是使用 gradle 实现的(里面包含了很多使用的姿势,可以看下

对于单元测试来说,测试Controller层的时候,只需要启动MVC相关的配置,不需要启动数据库连接;测试Service层的时候,既不需要启动数据库连接,也不需要启动web容器;测试Dao层的时候,就应该只关心Sql语句的逻辑,不应该去关心Controller和Service层的逻辑,所以单元测试的时候需要大量的Mock和Stub。

二、项目

2.1、基础项目

既然是 springBoot项目,就会有最基础的 MVC三层,那么我们需要搭建一个基础项目controllerservice以及mapper,并保证项目是没有问题的。我这里就使用 springBoot + mybatisPLus + mysql 即可。

2.1.1、pox.xml

基础的依赖如下

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.gxm</groupId>
    <artifactId>spock-maven</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spock-maven</name>
    <description>spock-maven</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 数据库操作 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- 工具类 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.11</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.51</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2.1.2、springBoot的application.yml文件

server:
  port: 8087
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/db_one?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: 123456
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      initial-size: 3
      min-idle: 3
      max-active: 20
      max-wait: 30000
mybatis-plus:
  mapper-locations: classpath:/mapper/*/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2.1.3、对应的三层基础目录

在这里插入图片描述
并写一个接口,确认基础项目没有问题
在这里插入图片描述

2.2、增加spock处理

2.2.1、pom.xml 增加依赖

pom.xml 则需要添加一些依赖,注意这里面有一个依赖的问题就是 mybatis-plus-boot-starter-test(后面有一个test,没有test的是主业务,前面的基础项目里面以及引入的,这个是mybatis-plus为集成单元测试做的准备),而且还要注意这个依赖是 mybatis-plus 3.4.0 开始引入的,里面提供了一个注解 @MybatisPlusTest,来自动注入mapper的实现,而对于mapper层的测试,我们是不希望污染我们的开发或者测试数据库的,所以需要使用 内存数据库H2

如果你没有使用mybatis-plus,而是使用 最基础的mybatis,那么你的测试的依赖就要换成 mybatis-spring-boot-starter-test,下面的pom文件中注释也说明了

        <!-- mybatis-plus 配合dao层测试 使用 @MybatisPlusTest  -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter-test</artifactId>
            <version>3.4.0</version>
            <scope>test</scope>
        </dependency>

        <!-- 要是 使用的 mybatis 就加入下面以来 使用 @MybatisTest注解 ,注意版本-->
<!--        <dependency>-->
<!--            <groupId>org.mybatis.spring.boot</groupId>-->
<!--            <artifactId>mybatis-spring-boot-starter-test</artifactId>-->
<!--            <version>2.0.1</version>-->
<!--            <scope>test</scope>-->
<!--        </dependency>-->

        <!-- 单元测试使用h2数据库 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.197</version>
        </dependency>


        <!-- Spock必须的  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.spockframework</groupId>
            <artifactId>spock-core</artifactId>
            <version>1.3-groovy-2.4</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.spockframework</groupId>
            <artifactId>spock-spring</artifactId>
            <version>1.3-RC1-groovy-2.4</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>2.4.6</version>
        </dependency>


        <!-- Spock不支持@InjectMocks和Mocks的组合,因此需要先引入Mockitio专门Spock开发的第三方插件  -->
        <dependency>
            <groupId>com.blogspot.toomuchcoding</groupId>
            <artifactId>spock-subjects-collaborators-extension</artifactId>
            <version>1.2.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.2.2、最终的pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.gxm</groupId>
    <artifactId>spock-maven</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spock-maven</name>
    <description>spock-maven</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 数据库操作 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>

        <!-- mybatis-plus 配合dao层测试 使用 @MybatisPlusTest  -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter-test</artifactId>
            <version>3.4.0</version>
            <scope>test</scope>
        </dependency>

        <!-- 要是 使用的 mybatis 就加入下面以来 使用 @MybatisTest注解 -->
<!--        <dependency>-->
<!--            <groupId>org.mybatis.spring.boot</groupId>-->
<!--            <artifactId>mybatis-spring-boot-starter-test</artifactId>-->
<!--            <version>2.0.1</version>-->
<!--            <scope>test</scope>-->
<!--        </dependency>-->


        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- 单元测试使用h2数据库 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.197</version>
        </dependency>


        <!-- Spock必须的  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.spockframework</groupId>
            <artifactId>spock-core</artifactId>
            <version>1.3-groovy-2.4</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.spockframework</groupId>
            <artifactId>spock-spring</artifactId>
            <version>1.3-RC1-groovy-2.4</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>2.4.6</version>
        </dependency>


        <!-- Spock不支持@InjectMocks和Mocks的组合,因此需要先引入Mockitio专门Spock开发的第三方插件  -->
        <dependency>
            <groupId>com.blogspot.toomuchcoding</groupId>
            <artifactId>spock-subjects-collaborators-extension</artifactId>
            <version>1.2.2</version>
            <scope>test</scope>
        </dependency>


        <!-- 工具类 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.11</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.51</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2.2.3、创建测试目录文件夹(重点一个坑)

因为我们使用groovy的语法来单元测试,而测试的时候,它只会在测试目录的 groovy 目录下找类,所以我们 必须先在测试目录下建立 groovy 文件夹,后续的spock的测试类都放在这里才行,然后选择项目结构,把这个目录设置为测试目录,不然你建立不了类,它变成黄色就说明可以了。

在这里插入图片描述

2.3、controller层单元测试

2.3.1、controller层原生代码

首先给出controller层的代码,注意我们一般在controller是不会直接使用mapper层,如果一定使用的话,也是可以的,单元测试的时候,直接把mapper给Mock就行。

package cn.gxm.spockmaven.controller;

import cn.gxm.spockmaven.dto.UnitTestDto;
import cn.gxm.spockmaven.pojo.UnitTest;
import cn.gxm.spockmaven.rsp.ResultItems;
import cn.gxm.spockmaven.service.UnitTestService;
import cn.hutool.core.collection.CollUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author GKJ
 * @version 1.0
 * @date 2021/9/10
 */
@RestController
public class UserController {

    @Autowired
    private UnitTestService unitTestService;

    @RequestMapping("/all")
    public ResultItems<UnitTest> all() {
        List<UnitTest> allAndAppendMockOne = unitTestService.getAllAndAppendMockOne();
        return ResultItems.onSuccess(allAndAppendMockOne, allAndAppendMockOne.size());
    }

    @RequestMapping("/list")
    public ResultItems<UnitTest> list(@RequestBody UnitTestDto unitTestDto) {
        if (unitTestDto == null || unitTestDto.getPage() == 0 ||
                unitTestDto.getSize() == 0) {
            return ResultItems.onError("page or size is not default");
        }
        List<UnitTest> allAndAppendMockOne = unitTestService.getAllAndAppendMockOne();
        return ResultItems.onSuccess(allAndAppendMockOne, allAndAppendMockOne.size());
    }

    @RequestMapping("/hasAllow")
    public ResultItems<UnitTest> hasAllow(@RequestBody UnitTestDto unitTestDto) {
        UnitTest result = unitTestService.hasAllow(unitTestDto);
        return ResultItems.onSuccess(CollUtil.newArrayList(result), 1);
    }
}

对应的测试代码,idea 是有快捷键的,鼠标移动到controller的类名上,alt + entry,选择 create Test

在这里插入图片描述

然后默认测试的类名,并选择父类为 spock.lang.Specification,并勾选对应需要单元测试的类
在这里插入图片描述
点击ok后,我们选择前面建立的groovy目录下,即可
在这里插入图片描述
还要注意的是创建出来的类,一定是 .groovy 文件,不是 .java 文件

2.3.2、controller层单元测试代码

注意测试代码文件是 .groovy 的。

Controller层的测试采用SpringBoot的注解**@WebMvcTest**,使用此注解后,测试类将会只启动和Controller有关的配置。注意,该注解只能用来测试SpringMvc写出来的Controller,还有一定

package cn.gxm.spockmaven.controller

import cn.gxm.spockmaven.dto.UnitTestDto
import cn.gxm.spockmaven.pojo.UnitTest
import cn.gxm.spockmaven.rsp.ResultItems
import cn.gxm.spockmaven.service.UnitTestService
import cn.hutool.http.HttpStatus
import com.alibaba.fastjson.JSON
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.http.MediaType
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
import spock.lang.Specification
import spock.lang.Title
import spock.lang.Unroll
import spock.mock.DetachedMockFactory

/**
 * @Author: GKJ
 * @Date: 2021/10/20
 * @Description:
 */
@Title("UserController 单元测试")
@WebMvcTest(controllers = [UserController.class])
class UserControllerTest extends Specification {

    @Autowired
    MockMvc mockMvc
    @Autowired
    UnitTestService unitTestService


    /**
     * 每一个测试执行前都会先执行它
     * @return
     */
    def setup() {
        def unitTestA = new UnitTest(1, "A")
        def unitTestB = new UnitTest(2, "B")
        unitTestService.allAndAppendMockOne >> [unitTestA, unitTestB]
    }


    /**
     * 这些测试会真的走controller 里面的业务逻辑
     * 遇到service#allAndAppendMockOne 时,就会使用上面mock的数据
     */
    @Unroll
    def "list success"() {
        given:
        def params = new UnitTestDto(1, 20)
        expect:
        mockMvc.perform(MockMvcRequestBuilders.get("/list").
                contentType(MediaType.APPLICATION_JSON).content(JSON.toJSONString(params)))
                .andExpect(MockMvcResultMatchers.status().isOk())
    }

    @Unroll
    def "list page or size is not set,will return empty list"() {
        given: ""
        def params = new UnitTestDto(0, 0)

        when: ""
        def response = mockMvc.perform(MockMvcRequestBuilders.get("/list").
                contentType(MediaType.APPLICATION_JSON).content(JSON.toJSONString(params)))
                .andReturn()

        then: ""
        System.out.println(response)
        // 获取
        def resultItem = JSON.parseObject(response.response.contentAsString, ResultItems.class)
        with(resultItem) {
            code == Integer.toString(HttpStatus.HTTP_INTERNAL_ERROR)
            msg == "page or size is not default"
        }
    }


    /**
     * 关于这个@TestConfiguration注解,是Spock官方建议的,
     * 在WebMvcTest的情况下使用,解决在声明变量的时候使用 Mock() 无效的问题
     */
    @TestConfiguration
    static class MockConfig {
        def detachedMockFactory = new DetachedMockFactory()

        @Bean
        UnitTestService unitTestService() {
            return detachedMockFactory.Mock(UnitTestService)
        }
    }

}
  • 关于这个 @TestConfiguration 注解,是Spock官方建议的,在WebMvcTest的情况下使用,解决在声明变量的时候使用Mock()无效的问题。

执行完毕后可以看到,且控制台没有打印我们初始化方法查找字典值的方法。节省加载初始化资源的时间。
在这里插入图片描述

2.4、service层单元测试

2.4.1、service层原生代码

  • UnitTestService 接口
package cn.gxm.spockmaven.service;

import cn.gxm.spockmaven.dto.UnitTestDto;
import cn.gxm.spockmaven.pojo.UnitTest;

import java.util.List;

/**
 * @Author: GKJ
 * @Date: 2021/10/20
 * @Description:
 */
public interface UnitTestService {

    /**
     * 获取所有后再添加一个模拟数据
     *
     * @return
     */
    List<UnitTest> getAllAndAppendMockOne();

    /**
     * 找到指定id 数据,并封装返回 dto 对象
     *
     * @param id id
     * @return dto 对象
     */
    UnitTestDto findOneByIdAndConvert2Dto(int id);

    UnitTest hasAllow(UnitTestDto unitTestDto);
}

  • UnitTestServicesImpl 实现类
package cn.gxm.spockmaven.service.impl;

import cn.gxm.spockmaven.dto.UnitTestDto;
import cn.gxm.spockmaven.mapper.UnitTestMapper;
import cn.gxm.spockmaven.pojo.UnitTest;
import cn.gxm.spockmaven.service.UnitTestService;
import cn.hutool.core.bean.BeanUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @Author: GKJ
 * @Date: 2021/10/20
 * @Description:
 */
@Component
public class UnitTestServiceImpl implements UnitTestService {

    @Autowired
    private UnitTestMapper unitTestMapper;

    @Override
    public List<UnitTest> getAllAndAppendMockOne() {
        List<UnitTest> unitTests = unitTestMapper.selectList(null);
        unitTests.add(new UnitTest(1, "zhuhai"));
        return unitTests;
    }

    @Override
    public UnitTestDto findOneByIdAndConvert2Dto(int id) {
        UnitTest unitTest = unitTestMapper.selectById(id);
        if (unitTest == null) {
            return null;
        }
        UnitTestDto unitTestDto = new UnitTestDto();
        BeanUtil.copyProperties(unitTest, unitTestDto);
        unitTestDto.setPage(1);
        unitTestDto.setSize(20);
        return unitTestDto;
    }

    @Override
    public UnitTest hasAllow(UnitTestDto unitTestDto) {
        return unitTestMapper.judgeAllow(unitTestDto.getId());
    }
}

在这里插入图片描述

2.4.2、service层单元测试代码

注意测试代码文件是 .groovy

  • 严格意义上讲,Service也算是一种特殊的@Component。其单测思维都近乎一样。
  • 正常情况下,Service层应该是纯代码业务逻辑,对数据库或者web容器都没有启动依赖。
package cn.gxm.spockmaven.service

import cn.gxm.spockmaven.mapper.UnitTestMapper
import cn.gxm.spockmaven.pojo.UnitTest
import cn.gxm.spockmaven.service.impl.UnitTestServiceImpl
import spock.lang.Specification
import spock.lang.Title

/**
 * @Author: GKJ
 * @Date: 2021/10/20
 * @Description: 严格意义上讲,Service也算是一种特殊的 @Component。其单测思维都近乎一样。
 正常情况下,Service层应该是纯代码业务逻辑,对数据库或者web容器都没有启动依赖
 */
@Title("service 层测试")
class UnitTestServiceTest extends Specification {

    def mapper = Mock(UnitTestMapper)
    // 注意这里使用的是具体的实现service
    def service = new UnitTestServiceImpl(unitTestMapper: mapper)


    def "findOneByIdAndConvert2Dto -> success -> 参数 #id,结果 #resutId,resultName"() {
        given: "mock mapper#selectById"
        // 注意这里的mock 方式参数时类型一定要一样,不然程序会认为这mock的不是同一个方法
        mapper.selectById(id as Serializable) >> new UnitTest(id, "a".concat(Integer.toString(id)))
//        mapper.selectById(_ as int) >> new UnitTest(id, "a".concat(Integer.toString(id)))

        when: "调用业务逻辑"
//        and:""
        def unitTestDto = service.findOneByIdAndConvert2Dto(id)


        then: ""
        with(unitTestDto) {
            id == resutId
            name == resultName
            page == 1
            size == 20
        }

        where: "分支测试"
        id   || resutId | resultName
        1    || 1       | "a1"
        2    || 2       | "a2"
        1098 || 1098    | "a1098"
    }
}

结果

在这里插入图片描述

2.5、mapper层单元测试

2.5.1、mapper层原生代码

  • 由于我用的是Mybatis-plus,所以我目前只会说**@MybatisPlusTest**注解相关。
  • 同时为了不用去启动真实的数据库,我采用H2DataBase内存数据库进行测试。
  • @MybatisPlusTest 的使用需要引入额外依赖,H2DataBase 同样也是,当然前面的那个完整pom文件里面以及包含了。这里只是再说一下


 <!-- mybatis-plus 配合dao层测试 使用 @MybatisPlusTest  -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter-test</artifactId>
            <version>3.4.0</version>
            <scope>test</scope>
        </dependency>

        <!-- 要是 使用的 mybatis 就加入下面以来 使用 @MybatisTest注解 -->
<!--        <dependency>-->
<!--            <groupId>org.mybatis.spring.boot</groupId>-->
<!--            <artifactId>mybatis-spring-boot-starter-test</artifactId>-->
<!--            <version>2.0.1</version>-->
<!--            <scope>test</scope>-->
<!--        </dependency>-->



<!-- 单元测试使用h2数据库 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.197</version>
        </dependency>
package cn.gxm.spockmaven.mapper;

import cn.gxm.spockmaven.pojo.UnitTest;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Component;

/**
 * @author GKJ
 * @version 1.0
 * @date 2021/9/13
 */
@Component
@Mapper
public interface UnitTestMapper extends BaseMapper<UnitTest> {

    /**
     * 是否允许
     *
     * @param id id
     * @return 对应数据,不允许则返回null
     */
    @Select("select * from unit_test where id = #{id}")
    UnitTest judgeAllow(@Param("id") int id);

    @Insert("insert into unit_test(name) values(#{unitTest.name})")
    int createDemo(@Param("unitTest") UnitTest unitTest);
}


2.5.2、增加单元测试的 application.yml

因为我们单元测试需要使用 h2数据库,所以我们需要获取项目的配置文件,加载h2数据库给mybatis-plus,建立文件 application-unittest.yml

其实这里应该使用数据库管理工具的比如 liquiBase 和 flyway等等,我会在其他文章中说明

#unit test 配置文件
server:
  port: 8087
spring:
  datasource:
    driver-class-name: org.h2.Driver
    #DATABASE_TO_UPPER的意思是,让数据库对大小写敏感,DB_CLOSE_DELAY=-1的意思是让数据库和JVM同生命周期
    url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;MODE=Mysql
    username: sa
    password:
    # 初始化数据库中的数据,可以没有 /每次运行在h2数据中创建对应的表
    schema: classpath:test/db/init_table.sql
  h2:
    console:
      path: /h2-console
      settings:
        web-allow-others: true
        trace: true
      enabled: true
mybatis-plus:
  mapper-locations: classpath:/mapper/*/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

我们还要建立h2每次运行后,执行的sql(这个sql文件就是为我们单元测试建立准备表和测试数据的,表格式和MySQL一致,只是这是内存,运行结束后就没有了)

init_table.sql 文件内容如下:


drop table if exists unit_test;

CREATE TABLE `unit_test` (
                             `id` int(11) NOT NULL AUTO_INCREMENT,
                             `name` varchar(100) DEFAULT NULL,
                             PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4

层次结构如下
在这里插入图片描述

2.5.3、mapper 层单元测试代码

注意测试代码文件是 .groovy

  • @ActiveProfiles(“unittest”)注解的作用:之前可以看到我们resources下面有两种配置文件,application.properites(以下简称主配置文件)里面我们配置的是Oracle数据库,而加了这个注解后,可以指定SpringBoot在启动的时候去寻找后标为“unittest”的配置文件,如果“unittest”配置文件里配了和主配置文件里相同的属性,则进行覆盖,否则沿用主配置文件的。

  • 关于@Rollback(false)注解:正常情况下,SpringBoot的测试用例每执行完一个就会回滚一次,比如说再执行完create entity user之后会立马将刚刚insert进去的数据清除,之后执行“get entity user”的时候应该是测试用例不通过的。加了这个注解后,就可以指定某个测试方法,或者某个测试类的执行结果不进行回滚。

package cn.gxm.spockmaven.mapper

import cn.gxm.spockmaven.pojo.UnitTest
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
import com.baomidou.mybatisplus.test.autoconfigure.MybatisPlusTest
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.annotation.Rollback
import org.springframework.test.context.ActiveProfiles
import spock.lang.Specification
import spock.lang.Title

/**
 * @Author: GKJ
 * @Date: 2021/10/20
 * @Description: mapper 层 测试
 *
 * 但是估计实际公司业务中并不会这样的,因为可能应该有对应的配置文件中心,比如apollo(其实也没有关系,不启动apollo的配置,还是启动我们这个单元测试的配置就行了)等等,
 * 到时候就要见招拆招了.....
 */

@Title("mapper 层测试")
// 注意这里要使用 unittest 的配置文件了,里面使用H2数据库
@ActiveProfiles("unittest")
@MybatisPlusTest
// 关于@Rollback(false)注解:正常情况下,SpringBoot的测试用例每执行完一个就会回滚一次,
// 比如说再执行完create entity test之后会立马将刚刚insert进去的数据清除,之后执行“get entity test”的时候应该是测试用例不通过的。
// 加了这个注解后,就可以指定某个测试方法,或者某个测试类的执行结果不进行回滚。
@Rollback(false)
class UnitTestMapperTest extends Specification {

    /**
     * 这里这个mapper 不能mock了,因为本身就是要测试它的逻辑的
     */
    @Autowired
    UnitTestMapper unitTestMapper

    void setup() {
    }

    void cleanup() {
    }

    def "create entity test"() {
        given: ""

        UnitTest unitTest = new UnitTest()
        unitTest.id = id
        unitTest.name = name

        expect: ""
        unitTestMapper.createDemo(unitTest) == 1

        where:
        id   | name
        1001 | "str1"
        2002 | "str2"

    }

    def "get entity test"() {
        expect:
        // 查询条件:名字中包含'ha'并且年龄小于40
        QueryWrapper<UnitTest> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name", "str1")
        unitTestMapper.selectOne(queryWrapper) != null
    }

}

在这里插入图片描述

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值