单元测试中使用Flyway时如何重置SQLite的AUTOINCREMENT自增序列
你之前尝试的方案不生效的核心原因:SQLite的AUTOINCREMENT字段的计数是存储在系统内置表sqlite_sequence中的,Flyway默认的clean操作只会清理用户创建的业务表,不会修改这个系统表的内容,所以即使你删除重建了Tag表,之前的自增计数仍然会被保留,导致新插入数据的ID逐次递增。
方案1:单元测试改用内存型SQLite(最推荐)
直接把单元测试环境的数据源配置为内存SQLite,每次测试启动时都会初始化一个全新的数据库实例,不存在任何历史数据残留,自然也不会有自增ID计数继承的问题,不需要额外修改Flyway配置。
Spring Boot测试配置示例(在src/test/resources/application.properties中添加):spring.datasource.url=jdbc:sqlite::memory: spring.datasource.driver-class-name=org.sqlite.JDBC方案2:在Flyway迁移脚本中补充重置自增计数逻辑
如果你必须使用文件型SQLite做测试,可以在创建Tag表的迁移脚本末尾,添加清理
sqlite_sequence对应记录的语句,每次建表后直接重置该表的自增起始值:
你的V1版本迁移脚本修改后如下:DROP TABLE IF EXISTS Tag; CREATE TABLE Tag( id INTEGER PRIMARY KEY AUTOINCREMENT, tag VARCHAR(255) NOT NULL UNIQUE, createdDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP, modifiedDate TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 新增以下语句重置自增计数 DELETE FROM sqlite_sequence WHERE name='Tag';该语句执行后,下一条插入Tag表的数据ID会从1开始计数,加上你预填充的2条数据,新增的ID必然为3,符合你的测试预期。
方案3:优化单元测试断言逻辑(最佳实践)
单元测试硬编码断言自增ID的具体值本身属于不够健壮的写法,你可以调整断言逻辑,避免依赖自增ID的固定值:
@Test public void add() { // 先查询当前已有数据的最大ID和条数 List<Tag> oldTags = this.tagDao.get(); Long maxIdBefore = oldTags.stream().mapToLong(Tag::getId).max().orElse(0L); int sizeBefore = oldTags.size(); Tag tagToAdd = new Tag("Tall"); Tag addedTag = this.tagDao.add(tagToAdd); assertNotNull(addedTag); // 改成断言ID比之前的最大值大1即可,不需要固定为3 assertEquals(maxIdBefore + 1, addedTag.getId()); assertEquals(tagToAdd.getTag(), addedTag.getTag()); List<Tag> tags = this.tagDao.get(); assertEquals(sizeBefore + 1, tags.size()); }这种写法即使后续你调整了预填充数据的条数,测试也不需要同步修改,兼容性更强。
方案4:修正@FlywayTest注解配置
你之前使用
@FlywayTest注解不生效,大概率是没有开启方法级别的清理,默认注解只会在测试类执行前清理一次,如果你单个类下有多个测试方法操作Tag表,就会出现计数累积。你可以修改注解参数,开启每个测试方法执行前都清理数据库:@FlywayTest(cleanForEachTest = true) public class TagDaoTest { // 测试方法 }搭配方案2的重置sqlite_sequence的语句,就能保证每次测试的自增计数都是从头开始。
内容的提问来源于stack exchange,提问作者Sandrew Cheru

