You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CI环境下Minitest中FactoryBot实例变量setup后被重实例化问题

Minitest系统测试CI环境实例变量ID异常问题解决

问题背景

使用以下技术栈进行Rails系统测试时,本地运行完全正常,但在GitHub Actions CI环境中出现异常:

  • Ruby v2.7.7
  • Rails v7.0.4.3
  • Factory_bot_rails v6.2.0
  • minitest v5.18.0

测试代码如下:

class SessionSystemTest < ApplicationSystemTestCase
  setup do
    @admin = FactoryBot.create :admin
    puts "setup admin: #{@admin.inspect}"
    @theID = @admin.id
    puts "the id: #{@theID}"
  end

  test "see admin" do
    puts "test admin: #{@admin.inspect}"
    puts "the id: #{@theID}"
    assert_equal @admin.id, @theID
   end
end

本地运行时,setup和测试中的@admin对象完全一致,测试通过;但CI环境中,@admin的ID被递增,@theID却保持不变,导致断言失败。

原因分析

核心问题在于系统测试的进程隔离特性:Minitest系统测试依赖Capybara启动独立的应用服务器进程,测试代码本身和应用服务器分属两个不同进程。本地环境可能因资源共享(如数据库连接池、内存缓存),让测试进程的@admin对象与应用服务器的记录状态保持一致;但CI环境进程隔离更严格,测试进程内存中的@admin对象不会同步应用服务器对数据库记录的修改。

此外,CI环境的数据库配置差异(如自增ID策略、测试隔离机制),或模型中存在隐式触发记录重新保存的回调/触发器,也可能导致该问题。

解决方案

1. 通过ID查询数据库获取最新记录(推荐)

系统测试中内存实例变量状态不可靠,最稳妥的方式是用保存的ID直接查询数据库,获取最新记录状态:

test "see admin" do
  admin = Admin.find(@theID)
  assert_equal admin.id, @theID
end

2. 创建后立即重载记录

如果需要在setup中保留可用实例变量,可在创建后调用reload,确保对象与数据库状态完全一致:

setup do
  @admin = FactoryBot.create(:admin).reload
  @theID = @admin.id
end

3. 检查测试数据库隔离配置

  • 禁用use_transactional_fixtures:系统测试中不建议使用事务隔离,因为Capybara的应用服务器进程看不到测试进程内的事务数据,改用database_cleaner gem管理测试数据,确保每个测试前后数据库状态干净。
  • 对齐CI与本地数据库配置:检查PostgreSQL的max_connections、MySQL的事务隔离级别等,确保CI环境数据库设置与本地一致。

4. 排查隐式保存逻辑

检查Admin模型的回调(如after_create、after_save)或数据库触发器,确认是否存在创建后自动修改并重新保存记录的逻辑,导致ID异常变化。

内容的提问来源于stack exchange,提问作者ERunner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 04:53:25