过去几天我一直在尝试测试这种方法而没有运气。
我希望能够做的另一件事是rescue
在最终重试尝试后出现的错误。
请参阅下面的评论和代码段。
Source code for retry_on is here as well for context.
这是示例代码和测试:
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_on
rails逻辑本身到测试我的业务逻辑。在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社区!
以下工作对我来说很好,也适用于多个测试用例和测试retry_on
块的副作用。
RSpec.describe MyJob, type: :job do
include ActiveJob::TestHelper
context 'when `MyError` is raised' do
before do
allow_any_instance_of(described_class).to receive(:perform).and_raise(MyError.new)
end
it 'makes 4 attempts' do
assert_performed_jobs 4 do
described_class.perform_later rescue nil
end
end
it 'does something in the `retry_on` block' do
expect(Something).to receive(:something)
perform_enqueued_jobs do
described_class.perform_later rescue nil
end
end
end
end
请注意,如果您允许异常在最后冒泡,则需要rescue nil
(或某种形式的救援)。
请注意,perform_now
不算作“排队工作”。所以做described_class.perform_now
导致assert_performed_jobs
计算的少一次尝试。
这是最终为我工作的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
是一个ActiveJob方法,每次执行retry_on
时都会运行,设置和检查。我们嘲笑它返回10然后期望它叫Bugsnag。一旦所有retry_on
都被满足,attempts
只会调用你在块中给出的东西。这样可行。
对于第二种情况,然后模拟为作业实例引发的错误。接下来我们检查它是否正确接收retry_job
(retry_on
在引擎盖下调用)以确认它正在做正确的事情。然后我们将perform_later
调用包裹在minitest
perform_enqueued_jobs
块中并称之为一天。
在第一个规范中
expect(MyJob).to receive(:retry_on).with(wait: 2.hours, attempts:3).exactly(3).times
这将永远无法工作,因为类方法retry_on
将在类初始化阶段被调用,即在将该类加载到内存中时,而不是在执行规范时
在第二个规范中,您尝试使用timecop使其工作,但仍然因为同样的原因而失败
第三个规范相对更现实,但是
assert_performed_jobs 3
没有通过块就行不通
就像是
assert_performed_jobs 2 do
//call jobs from here
end