Question

I'm writing a Rails system that ingests data from external sources by spawning multiple processes to fetch the data and updating a single db table. I want to write RSpec tests that spawn multiple processes that emulate the fetch/write process to look for concurrency issues.

short question

How can I initialize a table in an RSpec test so that an external process can see the contents of the table? (At least, I think that's the right question. Read on for details...)

longer form

The general structure of my RSpec test is:

it 'external task should update the correct records' do
  initialize_my_model_table_with_some_records
  spawn_external_tasks_to_update_records
  # wait for spawned processes to complete
  Process.waitall.each {|pid, status| status.exitstatus.should == 0 }
  validate_results
end

But the external process always sees the model table as empty (verified by debug printing). Subsequently, attempts to update the table fail.

I'm pretty sure this is because RSpec is holding the table under a lock so it can do a rollback after the test completes.

So (to repeat the short question): How can I initialize a table in an RSpec test so that an external process can see the initialized contents of the table?

edit #2

I notice that upon entry to a subsequent test, the table is in the state that the previous (external) processes left it. This makes sense: RSpec can only roll back the table to the state that it 'knows' about, so and changes made by external processes will persist.

This suggests a solution: It appears that it works to use a before(:all) to explicitly initialize the table. But is this is the cleanest approach?

environment

  • Ruby version 1.9.3 (x86_64-darwin10.8.0)
  • pg (0.13.2)
  • rails (3.2.1)
  • rspec (2.9.0)
  • rspec-rails (2.9.0)
Était-ce utile?

La solution

When RSpec runs a test, it locks the database under a transaction so that it can be rolled back after the test. As a result, any external process won't see changes that RSpec makes to the database. And correspondingly, any changes that an external process makes to the database won't be rolled back after the RSpec test.

One exception to this is inside before(:all) and after(:all) blocks: any changes that RSpec makes will be visible to external processes.

So the example in the OP can be made to work like this:

describe 'with external tasks' do
  before(:all)
    initialize_my_model_table_with_some_records
  end
  after(:all)
    reinitialize_my_model_table
  end

  it 'should update the correct records' do
    spawn_external_tasks_to_update_records
    # wait for spawned processes to complete
    Process.waitall.each {|pid, status| status.exitstatus.should == 0 }
    validate_results
  end
end
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top