一、前言
参考文章:
对于 spock
语法和 groovy
的语法,这里就不再多说了,以及对应的 mockito
和 powerMockito
,这里不再过多说明,后面有时间,会把这些文章给补全。
以上项目在 D:\code\exercise\unittest 目录下
- spock-maven 这个就是本篇文章说的
- unit-test 这个是使用 gradle 实现的(里面包含了很多使用的姿势,可以看下)
对于单元测试来说,测试Controller层的时候,只需要启动MVC相关的配置,不需要启动数据库连接;测试Service层的时候,既不需要启动数据库连接,也不需要启动web容器;测试Dao层的时候,就应该只关心Sql语句的逻辑,不应该去关心Controller和Service层的逻辑,所以单元测试的时候需要大量的Mock和Stub。
二、项目
2.1、基础项目
既然是 springBoot
项目,就会有最基础的 MVC
三层,那么我们需要搭建一个基础项目controller
,service
以及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
}
}