2012-05-18 17 views

Odpowiedz

25

Jednym ze sposobów obejścia tego problemu jest ręczne wywołanie zwrotne commit. Przykład:

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 

Trochę za późno, ale mam nadzieję, że to pomoże innym.

+0

Świetna sugestia. Dzięki. –

+0

Dziękuję bardzo. – baash05

2

This Gist pomógł mi.

To małpa łata ActiveRecord, aby uruchamiać wywołania zwrotne after_commit nawet przy użyciu urządzeń transakcyjnych.

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 

Umieść nowy plik w katalogu Rails.root/spec/support, np. .

Rails 3 automatycznie załaduje go w środowisku testowym.

+0

Ten pracował dla mnie, ale że moje specyfikacje unusably powolny. Zamierzam teraz zamienić na używanie funkcji after_save, chociaż jestem zaniepokojony, że nie zawsze może ona w 100% być zgodna z logiką biznesową. –

8

W moim przypadku rozwiązany taki problem z ustawieniami database_cleaner w umieszczonej poniżej:

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 

Dzięki Testing after_commit/after_transaction with Rspec

+0

W większości przypadków jest to właściwa odpowiedź, chociaż strategie usuwania i skracania są znacznie wolniejsze niż transakcje. – averell

7

ten jest podobny do użytkownika @ jamesdevar odpowiedzi powyżej, ale nie mogę dodać blok kodu, więc muszę zrobić osobny wpis.

Nie masz zmiany strategii dla całego zestawu specyfikacji. Możesz używać globalnie :transaction, po prostu użyj :deletion lub :truncation (oba działają) w razie potrzeby. Po prostu dodaj flagę do odpowiedniej specyfikacji.

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 

następnie w swoich specyfikacji:

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

To świetne rozwiązanie, które nie wymaga "run_callbacks" obiektu i nie trzeba instalować nowego klejnotu! –

Powiązane problemy