【问题标题】:Testing sidekiq perform_in with RSpec 3使用 RSpec 3 测试 sidekiq perform_in
【发布时间】:2014-10-04 09:47:15
【问题描述】:

RSpec 3 和 sidekiq 3.2.1。而且我已经正确设置了 sidekiq 和 rspec-sidekiq。

假设我有一个叫WeatherJob 的工人,它将天气状态从sunny 更改为rainy

class WeatherJob
  include Sidekiq::Worker

  def perform record_id
    weather = Weather.find record_id

    weather.update status: 'rainy'
  end
end

我是这样使用这个工人的:

WeatherJob.perform_in 15.minutes, weather.id.

在规范中,我使用 Timecop 来模拟时间:

require 'rails_helper'

describe WeatherJob do
  let(:weather) { create :weather, status: 'sunny' }
  let(:now)     { Time.current }

  it 'enqueue a job' do
    expect {
      WeatherJob.perform_async weather.id
    }.to change(WeatherJob.jobs, :size).by 1
  end

  context '15 mins later' do
    before do
      Timecop.freeze(now) do
        Weather.perform_in 15.minutes, weather.id
      end
    end

    it 'update to rainy' do
      Timecop.freeze(now + 16.minutes) do
        expect(weather.status).to eq 'rainy'
      end
    end
  end
end

我可以看到Weather.jobs 数组中有工作。时间是正确的 16 分钟后。但它没有执行任务?有什么建议吗?谢谢!

【问题讨论】:

标签: ruby rspec sidekiq rspec-sidekiq


【解决方案1】:

Sidekiq 有三种测试模式:disabledfakeinline。默认值为 fake,它只是将所有作业推送到作业数组中,这就是您所看到的行为。 inline 模式会立即运行作业,而不是将其排入队列。

要强制 Sidekiq 在测试期间内联运行作业,请将您的测试代码包装在 Sidekiq::Testing.inline! 块中:

before do
  Sidekiq::Testing.inline! do
    Timecop.freeze(now) do
      Weather.perform_in 15.minutes, weather.id
    end
  end
end

有关测试 Sidekiq 的更多信息,请参阅official Testing wiki page

【讨论】:

  • 我想测试我的作业是否已添加到数组中,并在 15 分钟后执行。使用inline! 不正确。
  • 您想测试您的作业是计划在 15 分钟后运行还是测试它是否真的在 15 分钟后运行?如果您想测试它是否运行inline 是您唯一的选择。
  • 感谢您的回复。我想按计划对其进行测试,并在 15 分钟后实际运行。
  • 你测试完了。测试你的功能,而不是 Sidekiq 的功能。
  • 不能同意最新的评论。如果我想保护代码免受意外更改怎么办?应该是某种测试延迟的方法。
【解决方案2】:

如果您想测试作业是否应该在 15 分钟后执行,那么您应该将测试用例分成两部分。第一部分,您应该测试它是否插入了在 15 分钟内激活的作业(使用模拟)。第二部分,作业是否被正确执行。

【讨论】:

    【解决方案3】:

    分两步完成。首先测试作业是否已调度,然后立即执行内联作业。这是一个例子

    it "finishes auction  (async)" do
      auction = FactoryGirl.create(:auction)
      auction.publish!
    
      expect(AuctionFinishWorker).to have_enqueued_sidekiq_job(auction.id).at(auction.finishes_at)
    end
    
    it "finishes auction  (sync)" do
      auction = FactoryGirl.create(:auction)
      auction.publish!
    
      Sidekiq::Testing.inline! do
        AuctionFinishWorker.perform_async(auction.id)
      end
    
      auction.reload
      expect(auction).to be_finished
    end
    

    have_enqueued_sidekiq_job 方法来自rspec-sidekiq gem。他们在develop 分支进行了积极的开发。确保你像这样包含它

      gem 'rspec-sidekiq', github: "philostler/rspec-sidekiq", branch: "develop"
    

    【讨论】:

      【解决方案4】:

      Weather.drain 可以解决问题

      require 'rails_helper'
      
      describe WeatherJob do
        let(:weather) { create :weather, status: 'sunny' }
        let(:now)     { Time.current }
      
        it 'enqueue a job' do
          expect {
            WeatherJob.perform_async weather.id
          }.to change(WeatherJob.jobs, :size).by 1
        end
      
        context '15 mins later' do
          before do
            Timecop.freeze(now) do
              Weather.perform_in 15.minutes, weather.id
            end
          end
      
          it 'update to rainy' do
            Timecop.freeze(now + 16.minutes) do
              Weather.drain
              expect(weather.status).to eq 'rainy'
            end
          end
        end
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-03-10
        • 2021-10-31
        • 1970-01-01
        • 2019-01-16
        • 2013-03-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多