首页 > 解决方案 > 如何使用 rspec 正确测试 ActiveJob 的 retry_on 方法?

问题描述

在过去的几天里,我一直在尝试测试这种方法,但没有成功。

我想做的另一件事是rescue在进行最后一次重试后冒泡的错误。

请在下面查看我的评论和代码片段。

retry_on 的源代码也在此处用于上下文。

这是示例代码和测试:

   my_job.rb

   retry_on Exception, wait: 2.hours, attempts: 3 do |job, exception|
   # some kind of rescue here after job.exceptions == 3  
   # then notify Bugsnag of failed final attempt.
   end

   def perform(an_object)
     an_object.does_something
   end

   my_spec.rb
   it 'receives retry_on 3 times' do
     perform_enqueued_jobs do
       expect(AnObject).to receive(:does_something).and_raise { Exception }.exactly(3).times
       expect(MyJob).to receive(:retry_on).with(wait: 2.hours, attempts: 3).exactly(3).times
       MyJob.perform_later(an_object)
     end
     assert_performed_jobs 3
   end

测试失败响应:

      1) MyJob.perform receives retry_on 3 times
         Failure/Error: expect(job).to receive(:retry_on).with(wait: 4.hours, attempts: 3).exactly(3).times

   (MyJob (class)).retry_on({:wait=>2 hours, :attempts=>3})
       expected: 3 times with arguments: ({:wait=>2 hours, :attempts=>3})
       received: 0 times
 # ./spec/jobs/my_job_spec.rb:38:in `block (4 levels) in <top (required)>'
 # ./spec/rails_helper.rb:48:in `block (3 levels) in <top (required)>'
 # ./spec/rails_helper.rb:47:in `block (2 levels) in <top (required)>'

我也尝试过使这项工作成为双重并存根该retry_on方法,但这也不起作用。

我还尝试使用 Timecop 来加快等待时间,但测试仍然失败:

           my_spec.rb
   it 'receives retry_on 3 times' do
     perform_enqueued_jobs do
       expect(AnObject).to receive(:does_something).and_raise { Exception }.exactly(3).times
       Timecop.freeze(Time.now + 8.hours) do
         expect(MyJob).to receive(:retry_on).with(wait: 2.hours, attempts: 3).exactly(3).times
       end
       MyJob.perform_later(an_object)
     end
     assert_performed_jobs 3
   end

这是一个类方法,ActiveJob我已经在byebug终端中确认了我的工作类就是这种情况。

这个测试不应该工作吗?它期望类接收带有某些参数的类方法。当我byebug将它放入retry_on块中时,我也会被击中,所以我知道该方法被多次调用。

几乎就好像它在另一个班级上被调用,这非常令人困惑,我不认为是这种情况,但我在这个问题上已经走到了尽头。

我几乎解决了这个问题,将我的测试从测试retry_onrails 逻辑本身解耦到围绕它测试我的业务逻辑。在rails改变retry_on逻辑的情况下,这种方式也更好。

但是,这不适用于多个测试用例。如果您在多个案例中使用它,最后一个测试将中断并说它执行的工作比预期的要多。

 my_spec.rb
 it 'receives retry_on 3 times' do
   perform_enqueued_jobs do
     allow(AnObject).to receive(:does_something).and_raise { Exception }
     expect(AnObject).to receive(:does_something).exactly(3).times
     expect(Bugsnag).to receive(:notify).with(Exception).once
     MyJob.perform_later(an_object)
   end
   assert_performed_jobs 3
 end

my_job.rb

retry_on Exception, wait: , attempts: 3 do |job, exception|
  Bugsnag.notify(exception)
end

def perform(an_object)
  an_object.does_something
end

对此的任何帮助/见解将不胜感激。

也会喜欢关于如何在最大尝试后处理冒泡异常的建议。我正在考虑在retry_on块内引发错误,然后discard_on触发引发的错误。

感谢精彩的 Stack Overflow 社区!

标签: ruby-on-railsrspecrspec-railsruby-on-rails-5.1rails-activejob

解决方案


retry_on这是最终为我工作所需的规格格式:

it 'receives retry_on 10 times' do
  allow_any_instance_of(MyJob).to receive(:perform).and_raise(MyError.new(nil))
  allow_any_instance_of(MyJob).to receive(:executions).and_return(10)
  expect(Bugsnag).to receive(:notify)
  MyJob.perform_now(an_object)
end

it 'handles error' do
  allow_any_instance_of(MyJob).to receive(:perform).and_raise(MyError.new(nil))
  expect_any_instance_of(MyJob).to receive(:retry_job)
  perform_enqueued_jobs do
    MyJob.perform_later(an_object)
  end
end

对于第一种情况, executions是每次retry_on执行时都会运行、设置和检查的 ActiveJob 方法。我们模拟它返回 10,然后期望它调用 Bugsnag。retry_on仅在满足所有条件后才调用您在块中提供的内容attempts。所以这行得通。

对于第二种情况,然后模拟为作业实例引发的错误。接下来我们检查它是否正确接收retry_jobretry_on在后台调用)以确认它正在做正确的事情。perform_later然后我们将调用包装在minitest perform_enqueued_jobs块中并收工。


推荐阅读