2012-05-18 78 views

回答

25

解決此問題的一個方法是手動觸發commit回調。例如:

describe SomeModel do 
    subject { ... } 

    context 'after_commit' do 
    after { subject.run_callbacks(:commit) } 

    it 'does something' do 
     subject.should_receive(:some_message) 
    end 
    end 
end 

有點晚了,但希望這有助於他人。

+0

很棒的建議。謝謝。 –

+0

非常感謝。 – baash05

2

This Gist幫助了我。

它猴子補丁ActiveRecord火災after_commit回調即使使用交易裝置。

module ActiveRecord 
    module ConnectionAdapters 
    module DatabaseStatements 
     # 
     # Run the normal transaction method; when it's done, check to see if there 
     # is exactly one open transaction. If so, that's the transactional 
     # fixtures transaction; from the model's standpoint, the completed 
     # transaction is the real deal. Send commit callbacks to models. 
     # 
     # If the transaction block raises a Rollback, we need to know, so we don't 
     # call the commit hooks. Other exceptions don't need to be explicitly 
     # accounted for since they will raise uncaught through this method and 
     # prevent the code after the hook from running. 
     # 
     def transaction_with_transactional_fixtures(options = {}, &block) 
     rolled_back = false 

     transaction_without_transactional_fixtures do 
      begin 
      yield 
      rescue ActiveRecord::Rollback => e 
      rolled_back = true 
      raise e 
      end 
     end 

     if !rolled_back && open_transactions == 1 
      commit_transaction_records(false) 
     end 
     end 
     alias_method_chain :transaction, :transactional_fixtures 

     # 
     # The @_current_transaction_records is an stack of arrays, each one 
     # containing the records associated with the corresponding transaction 
     # in the transaction stack. This is used by the 
     # `rollback_transaction_records` method (to only send a rollback hook to 
     # models attached to the transaction being rolled back) but is usually 
     # ignored by the `commit_transaction_records` method. Here we 
     # monkey-patch it to temporarily replace the array with only the records 
     # for the top-of-stack transaction, so the real 
     # `commit_transaction_records` method only sends callbacks to those. 
     # 
     def commit_transaction_records_with_transactional_fixtures(commit = true) 
     unless commit 
      real_current_transaction_records = @_current_transaction_records 
      @_current_transaction_records = @_current_transaction_records.pop 
     end 

     begin 
      commit_transaction_records_without_transactional_fixtures 
     rescue # works better with that :) 
     ensure 
      unless commit 
      @_current_transaction_records = real_current_transaction_records 
     end 
     end 
     end 
     alias_method_chain :commit_transaction_records, :transactional_fixtures 
    end 
    end 
end 

將這個新文件放到Rails.root/spec/support目錄中,例如, 。

Rails 3會自動將其加載到測試環境中。

+0

這爲我工作,但讓我的規格unusably緩慢。儘管我擔心在所有情況下可能不會100%與我的業務邏輯保持一致,但現在將交換使用after_save。 –

8

在我來說,我解決了與database_cleaner的設置這樣的問題放在下面:

config.use_transactional_fixtures = false 

config.before(:suite) do 
    DatabaseCleaner.strategy = :deletion 
    DatabaseCleaner.clean_with(:truncation) 
end 

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

config.after(:each) do 
    DatabaseCleaner.clean 
end 

由於Testing after_commit/after_transaction with Rspec

+0

這實際上是大多數情況下的正確答案,儘管刪除和截斷策略比事務慢得多。 – averell

7

這類似於上述@ jamesdevar的答案,但我不能添加一個代碼塊,所以我必須做一個單獨的條目。

您沒有改變整個規格套件的策略。您可以繼續在全球範圍內使用:transaction,然後根據需要僅使用:deletion:truncation(它們都可以工作)。只需在相關規範中添加一個標誌即可。

config.use_transactional_fixtures = false 

config.before(:suite) do 
    # The :transaction strategy prevents :after_commit hooks from running 
    DatabaseCleaner.strategy = :transaction 
    DatabaseCleaner.clean_with(:truncation) 
end 

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

然後,在你的規格:

describe "some test requiring after_commit hooks", :with_after_commit => true do 
+0

這是一個很好的解決方案,不涉及對象的'run_callbacks',而且您不必安裝新的gem! –

相關問題