单元测试实战(四)MyBatis-Plus 的测试

为鼓励单元测试,特分门别类示例各种组件的测试代码并进行解说,供开发人员参考。

本文中的测试均基于JUnit5。

单元测试实战(一)Controller 的测试

单元测试实战(二)Service 的测试    

单元测试实战(三)JPA 的测试

单元测试实战(四)MyBatis-Plus 的测试

单元测试实战(五)普通类的测试

单元测试实战(六)其它

概述

MyBatis Plus组件表现为Mapper对象(我们将不涉及IService的测试)。使用MyBatis/MyBatis-Plus的项目,往往有很多自写的SQL需要测试。

MyBatis Plus有专门的@MyBatisPlusTest注解,是苞米豆提供的功能,它是有Spring上下文的,使用JUnit的SpringExtension扩展类。与@DataJpaTest一样,它会在测试时自动将数据源替换为内存数据库的。

在本章的示例中,我们将展示一种使用SQL脚本来准备测试数据的方法。

断言应主要检查数据存取行为是否符合预期。

依赖

除依赖Spring自带测试框架外,还需要苞米豆的测试包以及内存数据库:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><scope>test</scope>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter-test</artifactId><version>3.5.3.2</version><scope>test</scope>
</dependency>
<dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>test</scope>
</dependency>

注意:mybatis-plus-boot-starter-test的3.5.2版本支持Spring Boot 2,3.5.3及以上支持Spring Boot 3。

示例

由于篇幅的关系,本章中我们将拿出一个Mapper方法作为示例。

以下为xml实现的一个查询方法:

<select id="selectPermissionCodesByUser" resultType="java.lang.String">SELECT p.codeFROM sys_auth_user_role urJOIN sys_auth_role_permission rp ON rp.role_id=ur.role_idJOIN sys_auth_permission p ON p.id=rp.permission_idWHERE p.type=2 AND ur.user_code=#{userCode} AND ur.asset_type=#{assetType} AND ur.asset_id=#{assetId}<if test="pkOrg != null">UNION DISTINCTSELECT p.codeFROM sys_auth_user_org uoJOIN sys_auth_role_permission rp ON rp.role_id=uo.role_idJOIN sys_auth_permission p ON p.id=rp.permission_idWHERE p.type=2 AND uo.user_code=#{userCode} AND uo.pk_org=#{pkOrg}</if>
</select>

以下为该查询的代理接口:

public interface SysAuthPermissionMapper extends BaseMapper<SysAuthPermission> {...Set<String> selectPermissionCodesByUser(@Param("userCode") String userCode,@Param("assetType") int assetType,@Param("assetId") long assetId,@Param("pkOrg") String pkOrg);...
}

以下是该查询方法的测试类:

package com.aaa.sdk.rbac.mybatis.database.mapper;import com.baomidou.mybatisplus.test.autoconfigure.MybatisPlusTest;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.init.ScriptUtils;
import org.springframework.test.context.TestPropertySource;import javax.sql.DataSource;
import java.sql.Connection;
import java.util.Set;import static org.assertj.core.api.Assertions.assertThat;@MybatisPlusTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestPropertySource(properties = {"spring.datasource.driver-class-name=org.h2.Driver","spring.datasource.url=jdbc:h2:mem:aaa_rbac_test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=MySQL;DATABASE_TO_LOWER=TRUE","spring.datasource.username=sa"
})
class SysAuthPermissionMapperTest {@Autowiredprivate SysAuthPermissionMapper mapper;@BeforeAllstatic void setupClass(@Autowired DataSource dataSource) throws Exception {try (Connection conn = dataSource.getConnection()) {ScriptUtils.executeSqlScript(conn, new ClassPathResource("/sql/test_schema.sql"));ScriptUtils.executeSqlScript(conn, new ClassPathResource("/sql/test_data.sql"));}}@Testvoid testSelectPermissionCodesByUser() {Set<String> permissions = mapper.selectPermissionCodesByUser("wangfei012", 1, 1, "0001A410000000A3I0V2");assertThat(permissions).hasSize(4);}@Testvoid testSelectPermissionCodesByUser_emptyPkOrg() {Set<String> permissions = mapper.selectPermissionCodesByUser("wangfei012", 1, 1, "");assertThat(permissions).hasSize(3);}@Testvoid testSelectPermissionCodesByUser_invalidUser() {Set<String> permissions = mapper.selectPermissionCodesByUser("nobody", 1, 1, "0001A410000000A3I0V2");assertThat(permissions).hasSize(0);}
}

测试类说明:

第18行,我们使用了@MyBatisPlusTest类注解。

第19行,为了H2数据库兼容MySQL语法,我们关闭了数据源的自动替换,选择用20-24行的代码来覆盖默认的数据源。

第28行,我们将代理接口注入测试类。

31行的setupClass方法会在所有测试方法执行之前执行一次,准备数据。这里我们用了Spring自带的ScriptUtils实用程序;其中sql脚本是在test目录下的resources文件夹;测试目录结构如下图:

上图中,MockApplication与测试在同一个包内,用来控制Spring上下文范围,以免启动真实的SpringBootApplication;它的代码如下(注意里面有个@MapperScan,用来发现当前包里的MyBatis mapper):

package com.aaa.sdk.rbac.mybatis.database.mapper;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@MapperScan(basePackages = {"com.aaa.sdk.rbac.mybatis.database.mapper"})
public class MockApplication {
}

以下继续测试类的说明:

从第38行开始,是selectPermissionCodesByUser方法的三个测试用例。

testSelectPermissionCodesByUser测试正常流程,包括全部4个正常参数。按照设计和测试数据,它应该取出4个结果。

testSelectPermissionCodesByUser_emptyPkOrg测试传入的pkOrg参数为空时的场景,覆盖的是xml文件中<if test="pkOrg != null">不满足的分支。按照设计和测试数据,它应该取出3个结果。

testSelectPermissionCodesByUser_invalidUser则是一个负面测试,测试当传入的userCode非法时的场景;它应该不返回任何结果。

与JPA的测试一样,测试类中没有Mock对象,因此也就不存在given - when - then三段式结构。(DAO对象没有更底层的依赖,因此不需注入Mock,这也使得它们看上去更像是一种与数据库的集成测试。)

总结

使用@MybatisPlusTest。

为了兼容MySQL语法而使用@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE),并用@TestPropertySource订制数据源属性。注意:兼容只是部分兼容,并非完全兼容。

使用@BeforeAll方法结合Spring自带的ScriptUtils准备测试数据。

MockApplication里加@MapperScan。

示例中,我们没有在每个方法之前重置测试数据,这是因为我们只测了查询方法吗?不。在DAO的测试中,不管是@MyBatisPlusTest还是@DataJpaTest,都带有@Transactional元注解,它们会在测试方法执行结束后自动回滚。

MyBatis-Plus 有所谓的 Lambda Query,可链式组合查询。我们不建议在Service的实现类里直接使用它;应该将其封装到DAO/Repository层的方法里,这样就方便mock这个封装好的方法的行为,避免直接mock链式组合查询行为。如果实在要mock链式组合查询行为,可以参考这篇和这篇。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/191538.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring学习③__Bean管理

目录 IOC接口ApplicationContext 详解IOC操作Bean管理基于xml方式基于xml方式创建对象基于xml方式注入属性使用set方法进行注入通过有参数的构造进行注入p 名称空间注入&#xff08;了解&#xff09; 基于xml方式注入其他类型属性xml 注入数组类型属性 IOC接口 IOC思想基于IOC…

机器人制作开源方案 | 智能快递付件机器人

一、作品简介 作者&#xff1a;贺沅、聂开发、王兴文、石宇航、盛余庆 单位&#xff1a;黑龙江科技大学 指导老师&#xff1a;邵文冕、苑鹏涛 1. 项目背景 受新冠疫情的影响&#xff0c;大学校园内都采取封闭式管理来降低传染的风险&#xff0c;导致学生不能外出&#xff0c…

原来机械硬盘比内存慢10万倍

我们都知道机械硬盘的速度很慢&#xff0c;内存的速度很快&#xff0c;那么不同存储器之间的差距到底有多大呢&#xff1f; 我们先来看一幅图&#xff1a; CPU访问寄存器的时间是0.3纳秒&#xff0c;访问L1高速缓存的时间是1纳秒&#xff0c;访问L2高速缓存的时间是4纳秒… 秒…

二维码智慧门牌管理系统升级解决方案:门牌聚合,让管理更便捷!

文章目录 前言一、传统门牌管理系统的瓶颈二、地图门牌聚合展示的优势三、地图门牌聚合展示的实现方法四、智慧门牌管理系统的未来发展 前言 随着城市的发展和建设&#xff0c;对于地址信息的管理变得越来越重要。而智慧门牌管理系统作为管理地址信息的重要工具&#xff0c;其…

Selenium自动化测试框架

一.Selenium概述 1.1 什么是框架? 框架&#xff08;framework&#xff09;是一个框子——指其约束性&#xff0c;也是一个架子——指其支撑性。是一个基本概念上的 结构用于去解决或者处理复杂的问题。 框架是整个或部分系统的可重用设计&#xff0c;表现为一组抽象构件及…

盘点60个Python各行各业管理系统源码Python爱好者不容错过

盘点60个Python各行各业管理系统源码Python爱好者不容错过 学习知识费力气&#xff0c;收集整理更不易。 知识付费甚欢喜&#xff0c;为咱码农谋福利。 源码下载链接&#xff1a;https://pan.baidu.com/s/1VdAFp4P0mtWmsA158oC-aA?pwd8888 提取码&#xff1a;8888 项目名…

java:IDEA中的Scratches and Consoles

背景 IntelliJ IDEA中的Scratches and Consoles是一种临时的文件编辑环境&#xff0c;用于写一些文本内容或者代码片段。 其中&#xff0c;Scratch files拥有完整的运行和debug功能&#xff0c;这些文件需要指定编程语言类型并且指定后缀。 举例&#xff1a;调接口 可以看到…

GCD:异步同步?串行并发?一文轻松拿捏!

GCD 文章目录 GCD进程线程进程与线程的关系进程与线程的区别 任务&#xff08;执行的代码&#xff09;队列线程与队列的关系 队列任务**同步执行任务&#xff08;sync&#xff09;**辅助方法**异步执行任务&#xff08;async)**总结栅栏任务迭代任务 队列详细属性QoSAttributes…

ARDUINO UNO 12颗LED超酷流水灯效果

效果代码&#xff1a; #define t 30 #define t1 20 #define t2 100 #define t3 50 void setup() { // set up pins 2 to 13 as outputs for (int i 2; i < 13; i) { pinMode(i, OUTPUT); } } /Effect 1 void loop() { effect_1(); effect_1(); effect_…

【Linux】第十七站:进程创建与进程终止

文章目录 一、进程创建1.fork函数2.写时拷贝3.批量化创建多个进程 二、进程终止1.进程退出场景2.进程退出的方法&#xff08;1&#xff09;exit和return&#xff08;2&#xff09;_exit和exit 一、进程创建 1.fork函数 在linux中fork函数时非常重要的函数&#xff0c;它从已存…

深信服AC密码认证(外部认证:LDAP认证)

拓扑图 搭建好自己AD域服务器&#xff0c;我搭建的服务器域名叫做liyanlongyu.com&#xff0c;如何搭建这里我就不做演示了 一.在AC中添加自己AD域服务器 二.添加LDAP认证策略 验证&#xff1a; 未认证发现&#xff0c;无法上网 点击网页&#xff0c;弹出认证页面 认证后&…

【C++】容器string的常用成员函数接口

目录 string - C Reference 1 容量相关 1.1 size/length 1.2 capacity 1.3 resize 1.4 reserve 1.5 empty 2 运算符重载 2.1 operator 2.2 operator[] 2.3 operator&#xff08;非成员函数&#xff09; 2.4 operator 2.5 operator>> && operator<…