モデルリードとコールバックがあります: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コールバックをテストする最良の方法は何ですか?
使用してみてください test_after_commit gem
または、spec/support/helpers/test_after_commit.rbに次のコードを追加します- Gist
私は Mihail Davydenkovのコメント が答えに値するものだと考えました:
subject.run_callbacks(:commit)
を使用することもできます。
また、この問題(トランザクションテストで呼び出されないコミットコールバック)はRails 5.0+)で修正する必要があることに注意してください。アップグレードします。参照: https://github.com/Rails/rails/pull/18458
私は DatabaseCleaner を使用しており、トランザクションとトランケーションを簡単に切り替えることができる構成で、速度のために前者が優先されますが、後者はコールバックのテストに使用できます。
RSpec before
およびafter
ハンドラーはスコープで機能するため、切り捨てをスコープにしたい場合は、before
ハンドラーを定義します。
config.before(:each, truncate: true) do
DatabaseCleaner.strategy = :truncation
end
そして、この構成をdescribe
、context
、または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
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
私はこのようなものを使います
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