Edmund Lee
Edmund Lee

Reputation: 2572

Database empty in new Thread / Process testing with Rspec + Factory Girl

Following this blog post, http://blog.arkency.com/2015/09/testing-race-conditions/

I am trying to test concurrency. But in my specs, I am not able to find the record when I spin up a new thread or fork a process.

describe 'test concurrency' do
  let(:order_1) { create(:order) }
  let(:order_2) { create(:order) }
  let(:order_3) { create(:order) }
  let(:order_4) { create(:order) }
  let(:product) { create(:product) }

  it 'test concurrency' do
    wait_for_all_threads = true
    product.update(quantity_available: 4, quantity_sold: 0, quantity_in_carts: 0)
    orders = [order_1, order_2, order_3, order_4]

    # Objects are persisted in here
    # because order_n.persisted => true
    # product.persisted => true
    # product.reload works without issue (and this is essentially what causes the RecordNotFound error in Order#add_item)

    threads = orders.map do |order|
      Thread.new do

        # Objects cannot be found in the database

        true while wait_for_all_threads

        item = order.add_item(product, 1)
        order_items << item
      end
    end

    wait_for_all_threads = false

    threads.each(&:join)

    # ...
    # here comes all the expects ...
    # not gonna post it because it's not what matters in this question
  end
end

After research, I've set these:

DatabaseCleaner.strategy = :truncation
config.use_transactional_fixtures = false

But no luck.

So the issue is, within the new thread, none of the created objects can be found in the database.

Some clues as I am digging:

Upvotes: 6

Views: 1607

Answers (1)

Dan
Dan

Reputation: 173

Using Rails 5 this worked for me. Place within the describe:

self.use_transactional_tests = false

"use_transactional_fixtures" has been deprecated.

I also had to bypass DatabaseCleaner via the following:

it 'tests concurrency', bypass_cleaner: true do

end

Then in rails_helper (or where you have DatabaseCleaner settings)

config.around(:each) do |example|
  if example.metadata[:bypass_cleaner]
    example.run
  else
    # database cleaner code
  end
end

I hope this helps you or somebody else out :)

Upvotes: 3

Related Questions