2012-05-18 14 views

Respuesta

25

Una forma de evitar esto es activar manualmente las devoluciones de llamada commit. Ejemplo:

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 poco tarde, pero espero que esto ayude a los demás.

+0

Gran sugerencia. Gracias. –

+0

Muchas gracias. – baash05

2

This Gist me ayudó.

Parchea ActiveRecord para activar las devoluciones de llamada after_commit incluso si usa dispositivos transaccionales.

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 

Ponga este nuevo archivo en su directorio Rails.root/spec/support, p. .

Rails 3 lo cargará automáticamente en el entorno de prueba.

+0

Esto funcionó para mí, pero hicieron las especificaciones de mi unusably lento. Voy a cambiar a usar after_save en lugar de ahora, aunque me preocupa que no se alinee al 100% con mi lógica comercial en todos los casos. –

8

En mi caso he resuelto tal problema con la configuración de database_cleaner colocado a continuación:

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 

Gracias a Testing after_commit/after_transaction with Rspec

+0

Esta es en realidad la respuesta correcta en la mayoría de los casos, aunque las estrategias de eliminación y truncamiento son mucho más lentas que las transacciones. – averell

7

Esto es similar a la respuesta de @ jamesdevar anteriormente, pero no podía añadir un bloque de código, así que tengo que hacer una entrada por separado.

No tiene que cambiar la estrategia para todo el conjunto de especificaciones. Puede seguir usando :transaction en todo el mundo y luego simplemente use :deletion o :truncation (ambos funcionan) según sea necesario. Solo agrega una bandera a la especificación relevante.

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 

entonces, en sus especificaciones:

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

Esta es una gran solución que no involucra el 'run_callbacks' del objeto y no es necesario instalar una nueva gema. –

Cuestiones relacionadas