枯燥的工作需要 SKILL 调剂,Spring Boot 中 Mybatis SQL 单元测试 SKILL

文章目录

    枯燥的 MES 重构工作,感觉没有尽头,调教 SKILL 反而成了唯一的乐趣。

    背景

    Spring Boot 功能代码开发完成后,可以让 AI 自动编译,通过编译日志来排查是否有问题,然后自动修复。
    但是,对于 Mybatis XML 文件中的 SQL 代码,在编译时通常排查不出问题,只有执行时才能发现数据库报错。例如:

    Error querying database. Cause: java.sql.SQLSyntaxErrorException: ORA-01722: 无效数字

    或者

    Error querying database. Cause: java.sql.SQLDataException: ORA-01861: 文字与格式字符串不匹配

    这些都是 AI 在实现代码时,经常会制造的一些 bug。虽然根源是没有把数据库表结构喂给 AI。

    但是,我依然想实现一个 Skill 能自动发现 Mybatis XML 文件中的 SQL 代码的问题,然后自动修复,省去了我来回在界面里点击,再把错误信息复制给 AI,如此反复的过程。

    需求

    这个 Skill 可以对指定功能的 mybatis xml 文件中的 SQL 做单元测试。

    H2 数据库还是 Oracle 数据库

    如果要模拟数据库操作,是使用 H2 数据库(内存数据库,支持各种数据库的 SQL)还是 Oracle 数据库呢?

    因为 SQL 强依赖 Oracle 专有语法(DECODE/TO_DATE)和真实表数据,只有连真实库才能真正验证。最后还是决定使用测试环境的 Oracle 数据库。

    create skill prompt

    这个 skill 接收功能名称,判断指定功能是否已经有代码实现了,如已实现,则自动创建对应的 mybatis xml 相关的 SQL 测试案例。如果已经有了单元测试,则按照该 skill 的最新标准修正单元测试。

    需要通过 python (oracledb 库)脚本连接测试数据库(jdbc:oracle:thin:@//x.x.x.x:1521/testdb),来判断相关表的表结构、字段类型。并插入测试数据,注意,只能使用测试数据库执行。绝对不可以连接正式生产数据库来执行上面的操作。

    同时把对应的表结构整理到 docs\database\tables.md 中, 使用 markdown 格式,二级标题是表名及对应的中文名,内容是表结构或者建表语句的 code block。目的是方便后续开发,或者调试 bug 时可以参考。

    写入的测试数据,不需要清理,因为测试数据库是独立的,不会影响生产环境。

    然后执行测试,如果测试通过,则提示测试通过,否则提示测试失败,并输出失败的测试案例名称。

    测试需要覆盖到各种前端查询条件的场景,即,各种前端查询条件的场景,需要写一个测试案例,测试案例名称是功能名称 + 前端查询条件的名称。

    测试流程

    • 先实现一个单元测试示例。确定方案选型
    • 实现第一版自动化测试 skill
    • 基于 skill 完善之前实现的单元测试示例

    意外惊喜

    AI 在编写 Skill 时,实际运行试点测试时,测试基础设施精确复现了你之前报告的 ORA-01861 错误——测试库有数据时 sn.created_at >= TO_DATE(?, ‘yyyy-MM-dd hh24:mi:ss’)(created_at 是 VARCHAR2)抛”文字与格式字符串不匹配”。这正验证了本 skill 的核心价值:真实测试库 + 有数据的表才能暴露空表单测发现不了的 SQL 问题。测试失败案例名为 queryByTimeRange。根因与修复已在 CODEBUDDY.md 记录(去掉 TO_DATE 改字符串比较)。

    需要能自动修复

    由于 plan 约束”不得改动业务源码”,我没有擅自修改 XML。需要我修复这个业务 XML 的 TO_DATE bug 吗? 修复后时间条件测试即可通过,年周号自检记录 在真实库的时间查询功能也将恢复正常。

    修改 skill 的规则,如果发现测试失败,则自动修复,允许改动业务源码。

    只运行单个测试类

    mvn -pl ruoyi-admin test "-Dtest=SomeFunctionMapperTest" "-Dskip.tests=false"
    

    运行某个包下的所有测试

    mvn -pl ruoyi-admin test "-Dtest=com.sunzhongwei.somefunction.*" "-Dskip.tests=false"
    

    运行所有 Mapper 测试

    mvn -pl ruoyi-admin test "-Dtest=*MapperTest" "-Dskip.tests=false"
    

    为何没有使用 MyBatis-Spring-Boot-Starter-Test

    https://mybatis.org/spring-boot-starter/mybatis-spring-boot-test-autoconfigure/zh_CN/index.html

    开发环境的版本: mybatis-plus-boot-starter 3.5.1

    1. 项目用了 Dynamic Datasource(多数据源)

    当前 RuoYi 项目生产配置用的是 dynamic-datasource-spring-boot-starter@DS 注解切数据源)。@MybatisTest 的自动配置期望的是单一 spring.datasource.* 数据源 + 默认的 SqlSessionFactory。而 dynamic-datasource 会自定义 DataSource 代理和 SqlSessionFactory,两者容易互相干扰——这也是为什么之前手工方案里特意用 DriverManagerDataSource 绕开 Druid/dynamic-datasource。

    2. MyBatis-Plus 的全局配置兼容性

    项目用的是 MyBatis-Plusmybatis-plus-boot-starter),而 mybatis-spring-boot-starter-test@MybatisTest 自动配置是基于原生 MyBatisSqlSessionFactory 来探测和加载的。官方 starter 对 MP 的兼容不是开箱即用,需要额外配置(如指定 MybatisSqlSessionFactoryBean、全局 MybatisConfiguration 等)。当前方案用的 MybatisSqlSessionFactoryBean + setTypeAliasesPackage 正是为了兼容 MP。

    3. 类型别名(SysConfig 等)需要手工指定

    @MybatisTest 默认不扫描 MP 的 typeAliasesPackage,之前我们踩过 SysConfig 别名解析失败的坑,手工配置里通过 setTypeAliasesPackage("com.sunzhongwei.domain") 明确解决了。

    关于作者 🌱

    我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊,或者关注我的个人公众号“大象工具”, 查看更多联系方式