• 单元测试实战(四)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自带测试框架外,还需要苞米豆的测试包以及内存数据库:

    1. <dependency>
    2. <groupId>org.springframework.bootgroupId>
    3. <artifactId>spring-boot-starter-testartifactId>
    4. <scope>testscope>
    5. dependency>
    6. <dependency>
    7. <groupId>org.junit.jupitergroupId>
    8. <artifactId>junit-jupiter-apiartifactId>
    9. <scope>testscope>
    10. dependency>
    11. <dependency>
    12. <groupId>com.baomidougroupId>
    13. <artifactId>mybatis-plus-boot-starter-testartifactId>
    14. <version>3.5.3.2version>
    15. <scope>testscope>
    16. dependency>
    17. <dependency>
    18. <groupId>com.h2databasegroupId>
    19. <artifactId>h2artifactId>
    20. <scope>testscope>
    21. dependency>

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

    示例

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

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

    1. <select id="selectPermissionCodesByUser" resultType="java.lang.String">
    2. SELECT p.code
    3. FROM sys_auth_user_role ur
    4. JOIN sys_auth_role_permission rp ON rp.role_id=ur.role_id
    5. JOIN sys_auth_permission p ON p.id=rp.permission_id
    6. WHERE p.type=2 AND ur.user_code=#{userCode} AND ur.asset_type=#{assetType} AND ur.asset_id=#{assetId}
    7. <if test="pkOrg != null">
    8. UNION DISTINCT
    9. SELECT p.code
    10. FROM sys_auth_user_org uo
    11. JOIN sys_auth_role_permission rp ON rp.role_id=uo.role_id
    12. JOIN sys_auth_permission p ON p.id=rp.permission_id
    13. WHERE p.type=2 AND uo.user_code=#{userCode} AND uo.pk_org=#{pkOrg}
    14. if>
    15. select>

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

    1. public interface SysAuthPermissionMapper extends BaseMapper {
    2. ...
    3. Set selectPermissionCodesByUser(@Param("userCode") String userCode,
    4. @Param("assetType") int assetType,
    5. @Param("assetId") long assetId,
    6. @Param("pkOrg") String pkOrg);
    7. ...
    8. }

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

    1. package com.aaa.sdk.rbac.mybatis.database.mapper;
    2. import com.baomidou.mybatisplus.test.autoconfigure.MybatisPlusTest;
    3. import org.junit.jupiter.api.BeforeAll;
    4. import org.junit.jupiter.api.Test;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
    7. import org.springframework.core.io.ClassPathResource;
    8. import org.springframework.jdbc.datasource.init.ScriptUtils;
    9. import org.springframework.test.context.TestPropertySource;
    10. import javax.sql.DataSource;
    11. import java.sql.Connection;
    12. import java.util.Set;
    13. import static org.assertj.core.api.Assertions.assertThat;
    14. @MybatisPlusTest
    15. @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
    16. @TestPropertySource(properties = {
    17. "spring.datasource.driver-class-name=org.h2.Driver",
    18. "spring.datasource.url=jdbc:h2:mem:aaa_rbac_test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=MySQL;DATABASE_TO_LOWER=TRUE",
    19. "spring.datasource.username=sa"
    20. })
    21. class SysAuthPermissionMapperTest {
    22. @Autowired
    23. private SysAuthPermissionMapper mapper;
    24. @BeforeAll
    25. static void setupClass(@Autowired DataSource dataSource) throws Exception {
    26. try (Connection conn = dataSource.getConnection()) {
    27. ScriptUtils.executeSqlScript(conn, new ClassPathResource("/sql/test_schema.sql"));
    28. ScriptUtils.executeSqlScript(conn, new ClassPathResource("/sql/test_data.sql"));
    29. }
    30. }
    31. @Test
    32. void testSelectPermissionCodesByUser() {
    33. Set permissions = mapper.selectPermissionCodesByUser("wangfei012", 1, 1, "0001A410000000A3I0V2");
    34. assertThat(permissions).hasSize(4);
    35. }
    36. @Test
    37. void testSelectPermissionCodesByUser_emptyPkOrg() {
    38. Set permissions = mapper.selectPermissionCodesByUser("wangfei012", 1, 1, "");
    39. assertThat(permissions).hasSize(3);
    40. }
    41. @Test
    42. void testSelectPermissionCodesByUser_invalidUser() {
    43. Set permissions = mapper.selectPermissionCodesByUser("nobody", 1, 1, "0001A410000000A3I0V2");
    44. assertThat(permissions).hasSize(0);
    45. }
    46. }

    测试类说明:

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

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

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

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

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

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

    以下继续测试类的说明:

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

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

    testSelectPermissionCodesByUser_emptyPkOrg测试传入的pkOrg参数为空时的场景,覆盖的是xml文件中不满足的分支。按照设计和测试数据,它应该取出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链式组合查询行为,可以参考这篇这篇。 

  • 相关阅读:
    安装 hbase(伪分布式)
    小白看过来,企业资产这样管理,绝了~
    【PAT乙级】一百一十道真题刷后大汇总——C/C++
    ES6 class类
    CentOS部署MySQL 5.7(详细)
    2年自动化测试经验,连基础的都不会,还是卷王中的测试天花板,就这?
    【Java数据结构】详解LinkedList与链表(四)
    由Redis Cluster集群引发的对几种算法的思考
    椎弓根三角新算法
    目标检测算法——收藏|小目标检测的定义(一)
  • 原文地址:https://blog.csdn.net/ioriogami/article/details/134481372