如何使用 rspec 正确测试 ActiveJob 的 retry_on 方法? [英] How to properly test ActiveJob's retry_on method with rspec?

查看:58
本文介绍了如何使用 rspec 正确测试 ActiveJob 的 retry_on 方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have been attempting to test this method for the past few days with no luck.

我希望能够做的另一件事是救援在最后一次重试尝试后出现的错误.

Another thing I'd like to be able to do is rescue the error that bubbles up after the final retry attempt is made.

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

Please see my comments and code snippets below.

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 方法,但这也不起作用.

I've also tried making the job a double and stubbing the retry_on method and that doesn't work either.

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

I've also tried using Timecop to fast forward the wait time and tests are still failing:

           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 终端中确认了我的工作类就是这种情况.

It IS a class method of ActiveJob and I've confirmed this in a byebug terminal that this is the case with my job class.

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

Shouldn't this test work? It's expecting the class to receive the class method with certain arguments. My byebug gets hit when I put it in the retry_on block as well so I know that the method is getting called multiple times.

这几乎就像是在一个不同的班级上被调用,这非常令人困惑,我不认为是这种情况,但我对这个已经束手无策了.

It's almost as if it's being called on a different class which is very confusing and I don't think is the case but I'm at the end of my rope with this one.

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

I almost resolved the issue by decoupling my tests from testing the retry_on rails logic itself to testing my business logic around it. This way is better as well in the case that rails ever changes the retry_on logic.

然而,这对于多个测试用例不起作用.如果您将此用于多个案例,则最后一个测试会中断并说它执行的作业比预期的多.

HOWEVER, this does NOT work for more than one test case. If you use this with more than one case, the last test will break and say it has performed more jobs than expected.

 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

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

Any help/insight on this would be greatly appreciated.

也希望得到有关如何在最大尝试后处理冒泡异常的建议.我正在考虑在 retry_on 块中引发错误,然后为引发的错误触发 discard_on 触发器.

Would also love a recommendation on how to handle the bubbled up exception after max attempts too. I'm thinking of raising an error within the retry_on block and then have discard_on trigger for the error that's raised.

感谢精彩的 Stack Overflow 社区!

Thank you wonderful Stack Overflow community!

推荐答案

这是最终对我有用的 retry_on 所需的规范格式:

This is the format of specs needed for retry_on that finally worked for me:

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 是一个 ActiveJob 方法,每次执行 retry_on 时都会运行、设置和检查.我们模拟它返回 10,然后期望它调用 Bugsnag.retry_on 仅在满足所有 attempts 后才调用您在块中提供的内容.所以这是有效的.

For the first case, executions is an ActiveJob method that gets run, set and checked every time retry_on is executed. We mock it to return 10 and then expect it to call Bugsnag. retry_on only calls what you gave it in the block once all the attempts have been met. So this works.

对于第二种情况,然后模拟为作业实例引发的错误.接下来,我们检查它是否正确接收了 retry_job(retry_on 在幕后调用)以确认它正在做正确的事情.然后我们将 perform_later 调用包装在 minitest perform_enqueued_jobs 块中,并称之为一天.

For the second case, Then mock the error to raise for the job instance. Next we check that it's correctly receiving retry_job (which retry_on calls under the hood) to confirm it's doing the right thing. Then we wrap the perform_later call in the minitest perform_enqueued_jobs block and call it a day.

这篇关于如何使用 rspec 正确测试 ActiveJob 的 retry_on 方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆