私はこの方法をこの数日間、運のない状態でテストしようとしています。
私ができるもう1つのことは、最後の再試行が行われた後に発生するエラーrescue
です。
以下のコメントとコードスニペットをご覧ください。
これがサンプルコードとテストです:
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
それはIS 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
に必要な仕様の形式で、最終的に私のために機能しました:
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
が満たされた場合にのみ、ブロックで指定したものを呼び出します。これでうまくいきます。
2番目のケースでは、ジョブインスタンスに対して発生するエラーを模擬します。次に、retry_job
(内部でretry_on
が呼び出されます)が正しく受信されていることを確認して、正しく動作していることを確認します。次に、perform_later
呼び出しをminitest
perform_enqueued_jobs
ブロックで囲み、1日と呼びます。
以下は、私、複数のテストケース、および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
によってカウントされる試行が1つ少なくなります。
最初の仕様では
expect(MyJob).to receive(:retry_on).with(wait: 2.hours, attempts:3).exactly(3).times
クラスメソッドretry_on
がクラスの初期化フェーズで呼び出されるため、これは機能しません。つまり、スペックの実行中ではなく、そのクラスをメモリにロードするとき
2番目の仕様では、timecopを使用して機能させようとしましたが、同じ理由で失敗しました
3番目の仕様は比較的現実的ですが、
assert_performed_jobs 3
ブロックを渡さないと動作しません
何かのようなもの
assert_performed_jobs 2 do
//call jobs from here
end
私見あなたはRailsチームと一緒にActiveJobのテストを離れるべきです。
ジョブを適切に構成していることを確認するだけです:
it 'retries the job 10 times with 2 minutes intervals' do
allow(MyJob).to receive(:retry_on)
load 'app/path/to/job/my_job.rb'
expect(MyJob).to have_received(:retry_on)
.with(
Exception,
wait: 2.minutes,
attempts: 10
)
end