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

含Ruby Thread的Sidekiq Worker执行RSpec测试失败该如何处理

RSpec测试含Ruby多线程代码的解决方案

问题根源

对象为空报错并非RSpec不支持多线程,核心原因是默认开启的事务性测试机制:

RSpec默认的事务性测试会将所有测试操作放在一个未提交的数据库事务中,事务仅绑定测试运行的主线程,自定义创建的Thread子线程无法读取主线程中未提交的事务数据,因此读取对象时返回空。


方案1:单元测试场景(推荐优先使用)

单元测试仅需验证业务逻辑正确性,无需实际触发多线程运行,直接Stub线程方法即可,无需修改测试框架配置,运行速度快,完全规避多线程数据可见性问题。

修改后的测试用例示例:

it 'creates notification' do
  # 新增Stub逻辑:让Thread.new直接同步执行代码块,不创建真实子线程
  allow(Thread).to receive(:new) do |&block|
    block.call
  end

  job = create(:job)
  notification = create(:notification)
  devices = create(:device)
  scores = ScoringService.new(job, 25).score
  worker = ScoreWorker.new
  expect do
    worker.perform(scores, notification, devices)
  end.to change(Notification, :count).by(1)
end

方案2:需要真实测试多线程行为的场景

如果要验证多线程运行的实际效果,需要关闭事务性测试,改用数据库截断策略,让所有线程都能读取到已写入数据库的真实数据。

第一步:配置数据库清理策略

在spec/rails_helper.rb中添加如下配置:

RSpec.configure do |config|
  # 关闭全局事务性测试
  config.use_transactional_fixtures = false

  config.before(:suite) do
    DatabaseCleaner.strategy = :truncation
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end
end

第二步:调整原测试用例

删除测试代码中的self.use_transactional_fixtures配置即可,其余逻辑无需改动。

注意:截断策略会真实写入并清空数据库,测试运行速度会比事务策略慢,仅在必要时使用。


额外优化建议

现有Worker的多线程实现没有利用到scores参数,每个线程执行的逻辑完全相同,会重复推送相同通知,建议调整为每个线程处理单条score数据:

def perform(scores, notification, devices)
  threads = []
  scores.each do |score|
    # 把score传入服务,避免重复执行相同逻辑
    threads << Thread.new { PushNotificationService.new(notification, devices, score).deliver }
  end
  threads.each(&:join)
end

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 19:12:02