Подтвердить что ты не робот

Тестирование after_commit с RSpec и насмешкой

У меня есть модель Lead и обратный вызов: 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

Каков наилучший способ проверить after_commit обратные вызовы в Rails?

4b9b3361

Ответ 1

Попробуйте использовать test_after_commit gem

или добавить следующий код в spec/support/helpers/test_after_commit.rb - Gist

Ответ 2

Я думал, комментарий Михаила Давыденкова заслуживает ответа:

Вы также можете использовать subject.run_callbacks(:commit).

Также обратите внимание, что эта проблема (совершать обратные вызовы, которые не вызываются в транзакционных тестах) должна быть исправлена ​​в rails 5.0+, поэтому вы можете сделать заметку, чтобы удалить любые обходные пути, которые вы можете использовать в то же время при обновлении. См.: https://github.com/rails/rails/pull/18458

Ответ 3

Я использую 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

Ответ 4

Обновление для 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