Why after_commit not running even with use_transactional_fixtures = false

后端 未结 5 2351
悲哀的现实
悲哀的现实 2021-02-19 03:03

Transactional fixtures in rspec prevent after_commit from being called, but even when I disable them with

RSpec.configure do |config|
  config.use_transactional_         


        
相关标签:
5条回答
  • 2021-02-19 03:37

    This is similar to @jamesdevar's answer above, but I couldn't add a code block, so I have to make a separate entry.

    You don't have the change the strategy for the whole spec suite. You can keep using :transaction globally then just use :deletion or :truncation (they both work) as needed. Just add a flag to the relevant spec.

    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
    

    then, in your specs:

    describe "some test requiring after_commit hooks", :with_after_commit => true do
    
    0 讨论(0)
  • 2021-02-19 03:51

    This Gist helped me.

    It monkey-patches ActiveRecord to fire after_commit callbacks even if using transactional fixtures.

    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
    

    Put this a new file in your Rails.root/spec/support directory, e.g. spec/support/after_commit_with_transactional_fixtures.rb.

    Rails 3 will automatically load it in the test environment.

    0 讨论(0)
  • 2021-02-19 03:53

    In my case I resolved such problem with database_cleaner's settings placed below:

    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
    

    Thanks to Testing after_commit/after_transaction with Rspec

    0 讨论(0)
  • 2021-02-19 03:54

    If you're using database_cleaner you'll still run into this. I'm using the test_after_commit gem, and that seems to do the trick for me.

    0 讨论(0)
  • 2021-02-19 03:55

    One way around this is to trigger the commit callbacks manually. Example:

    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
    

    A little late, but hope this helps others.

    0 讨论(0)
提交回复
热议问题