web-dev-qa-db-ja.com

RSpecとモッキングを使用したafter_commitのテスト

モデルリードとコールバックがあります:after_commit :create, :send_to_SPL

Rails-4.1.0、Ruby-2.1.1、RSpecを使用しています。

1)この仕様は不合格です。

context 'callbacks' do
  it 'shall call \'send_to_SPL\' after create' do
    expect(lead).to receive(:send_to_SPL)
    lead = Lead.create(init_hash)
    p lead.new_record? # => false
  end
end

2)この仕様も通過していません:

context 'callbacks' do
  it 'shall call \'send_to_SPL\' after create' do
    expect(ActiveSupport::Callbacks::Callback).to receive(:build)
    lead = Lead.create(init_hash)
  end
end

3)これは合格ですが、after_commitコールバックをテストしていないと思います:

context 'callbacks' do
  it 'shall call \'send_to_SPL\' after create' do
    expect(lead).to receive(:send_to_SPL)
    lead.send(:send_to_SPL)
  end
end

Railsでafter_commitコールバックをテストする最良の方法は何ですか?

31

使用してみてください test_after_commit gem

または、spec/support/helpers/test_after_commit.rbに次のコードを追加します- Gist

39
Oleg Haidul

私は Mihail Davydenkovのコメント が答えに値するものだと考えました:

subject.run_callbacks(:commit)を使用することもできます。

また、この問題(トランザクションテストで呼び出されないコミットコールバック)はRails 5.0+)で修正する必要があることに注意してください。アップグレードします。参照: https://github.com/Rails/rails/pull/18458

47
ihaztehcodez

私は DatabaseCleaner を使用しており、トランザクションとトランケーションを簡単に切り替えることができる構成で、速度のために前者が優先されますが、後者はコールバックのテストに使用できます。

RSpec beforeおよびafterハンドラーはスコープで機能するため、切り捨てをスコープにしたい場合は、beforeハンドラーを定義します。

config.before(:each, truncate: true) do
  DatabaseCleaner.strategy = :truncation
end

そして、この構成をdescribecontext、またはitブロックに使用するには、次のように宣言する必要があります。

describe "callbacks", truncate: true do
   # all specs within this block will be using the truncation strategy
  describe "#save" do
    it "should trigger my callback" do
      expect(lead).to receive(:send_to_SPL)
      lead = Lead.create(init_hash)
    end
  end
end

完全なフック構成:(spec/support/database_cleaner.rbに保存)

RSpec.configure do |config|
  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end

  config.before(:each, truncate: true) do
    DatabaseCleaner.strategy = :truncation
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.append_after(:each) do
    DatabaseCleaner.clean
  end
end
6
Koen.

Rails5の更新。

コールバック処理は確かに修正されていますが、それでも#reloadを自由に使用する必要がある場合があります。

例:
作成後のコールバックを次のように定義するモデルがあるとします。

after_create_commit { assign_some_association }

この動作は次のように指定できます。

describe "callbacks" do
  describe "assigning_some_association" do
    subject(:saving) { record.save!; record.reload } # reload here is important

    let(:record) { build(:record) }

    it "assigns some association after commit" do        
      expect{ saving }.to(
        change{ record.some_association_id }.from(nil).to(anything)
      )
    end
  end
end
2
Epigene

私はこのようなものを使います

describe 'some method on record' do
  let(:record) { create(:some_record) }
  let(:update_block) { ->(record) { record.save! } } # define an labmda that will be called in a transaction block
  let(:result_method) { :some_method } # define a method to be called
  let(:result) do
    record.class_eval <<~EVAL, __FILE__, __LINE__ + 1
      after_commit :_record_result
       def _record_result
        @_result = public_send(:#{result_method})
      end
    EVAL

    record.transaction do
      update_block.call(record)
    end

    record.instance_variable_get(:'@_result')
  end

  before do
    # apply changes to record
  end

  it 'returns the correct result' do
    expect(result).to eq(some_value)
  end
end
0