ActiveJob#perform_laterにモデルのインスタンスを渡すテストを書く

井原(@ihara2525)です。

以下のような、インスタンスが変更されるとActivejobでElasticsearchのインデックスを更新する処理があり、そのテストを書きたい!ということでやってみました。

after_commit -> { ElasticsearchIndexerJob.perform_later('index', self) if published? }, on: :create

ActiveJob::TestHelperという便利なヘルパが用意されているので、まずはそれをinclude。

spec/support/active_job.rb

RSpec.configure do |config|
  config.include ActiveJob::TestHelper
end

ジョブが保存される場合はその内容をチェック、保存されない場合はキューが空であることをテストしています。

require 'rails_helper'

RSpec.shared_examples_for 'indexable' do
  shared_examples_for 'indexed' do
    before { clear_enqueued_jobs }

    it 'is in indexing queue' do
      subject.save
      expect(enqueued_jobs.size).to eq 1
      expect(enqueued_jobs.first[:job]).to eq ElasticsearchIndexerJob
      expect(enqueued_jobs.first[:args]).to eq [operation, { '_aj_globalid' => subject.to_global_id.to_s }]
      expect(enqueued_jobs.first[:queue]).to eq 'default'
    end
  end

  shared_examples_for 'not indexed' do
    before { clear_enqueued_jobs }

    it 'is not in indexing queue' do
      subject.save
      expect(enqueued_jobs.size).to eq 0
    end
  end

  context 'when the source is created' do
    let(:operation) { 'index' }

    context 'when the source is published' do
      before { subject.published_at = Time.now }

      it_behaves_like 'indexed'
    end

    context 'when the source is not published' do
      it_behaves_like 'not indexed'
    end
  end
end

実際perform_laterには'index', selfを渡しているので(selfはincludeされているモデルのインスタンス)、ActiveJob::TestHelperに用意されているassert_enqueued_withを使って、

assert_enqueued_with(job: ElasticsearchIndexerJob, args['index', subject], queue: 'default') { subject.save }

のように書きたかったのですが、キューにはGlobal IDが入っているため、上記のようにGlobal IDと比較する、若干内部実装によったテストになってしまっています。

これがベストなのかわかりませんが、perform_laterが呼び出されることをチェックするよりは良くなっているように思います!