2012-05-18 16 views

risposta

25

Un modo per aggirare questo è attivare i callback commit manualmente. Esempio:

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 

Un po 'in ritardo, ma spero che questo aiuti gli altri.

+0

Ottimo suggerimento. Grazie. –

+0

Grazie mille. – baash05

2

This Gist mi ha aiutato.

It patch-patch ActiveRecord attiva i callback after_commit anche se si utilizzano dispositivi transazionali.

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 

Inserire questo nuovo file nella directory Rails.root/spec/support, ad es. .

Rails 3 lo caricherà automaticamente nell'ambiente di test.

+0

questo ha funzionato per me, ma ha reso la mia spec unusably lento. Passare a utilizzare after_save invece per ora, anche se sono preoccupato che potrebbe non allinearsi al 100% con la mia logica aziendale in tutti i casi. –

8

Nel mio caso ho risolto tale problema con le impostazioni di database_cleaner posizionata al di sotto:

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 

Grazie alla Testing after_commit/after_transaction with Rspec

+0

Questa è in realtà la risposta corretta nella maggior parte dei casi, sebbene le strategie di eliminazione e di troncamento siano molto più lente delle transazioni. – averell

7

Questo è simile a @ di jamesdevar risposta di cui sopra, ma non ho potuto aggiungere un blocco di codice, quindi devo fare una voce separata.

Non si ha la modifica della strategia per l'intera suite di specifiche. È possibile continuare a utilizzare :transaction a livello globale quindi utilizzare solo :deletion o :truncation (funzionano entrambi) in base alle esigenze. Basta aggiungere una bandiera alle specifiche pertinenti.

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 

poi, nelle specifiche:

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

Questa è un'ottima soluzione che non coinvolge 'run_callbacks 'dell'oggetto e non è necessario installare una nuova gemma! –

Problemi correlati