Minitest::Parallel::Db
Run Minitest in parallel with a single database.
Rationale
We should be able to run tests in parallel even if there is a database involved. We can leverage database transactions to keep our tests isolated from each other.
There are solutions out there already, but some require creating multiple databases or running multiple processes. None of that sounds appealing to me.
The solution
Tests in parallel must be run in isolation. If the tests are sharing the same database and table, we have a problem. But if we can use a database transaction for each test, they will be run in isolation from each other (although write locks are still in effect).
Usage
require 'minitest/parallel/db'
Minitest::Test.send(:include, Minitest::Parallel::DB::ActiveRecord)
# Set number of threads you want. Best to match your pool size.
# Minitest defaults this to 2.
Minitest::Parallel::Db.concurrency = 10
describe 'your parallel tests' do
it 'saves a record' do
model = Model.create!
Model.last.id.must_equal model.id
end
it 'edits a record' do
Model.create!
Model.last.update_attributes(name: 'changed')
Model.last.name.must_equal 'changed'
end
end
Requirements
- Postgres (should work with databases that support transactions, but I haven't tried any)
- Minitest >= 4.2 (where
parallelize_me!
exists) - Supported ORM (ActiveRecord, Sequel)
- Get rid of DatabaseCleaner if you're using it.
Minitest versions
At the time this gem was created, Minitest was at v4.7.5. Since then, the way to set the number of concurrent tests to be run has changed. Please let me know if the version of Minitest you are using doesn't work with this gem so I can try to patch it.
Rails
If you are using a recent enough version of Rails, you don't need this gem. See the Rails section of my blog post about this.
Tips
Use sequences with factories to avoid write blocks on unique fields
If you have a field that is unique (i.e. the database defines it as unique, not just unique by a model validation), use sequences in your factories. Unique indexes will be enforced by the database (even if the writes are in their own transactions), and it will block writes, slowing you down a bit. To avoid that, use sequences where you can to easily avoid duplicates.
factory :users do
sequence(:username) { |idx| "user #{idx}" }
end
Running tests for this gem
The safe way: run each of the scripts listed in .travis.yml
.
The quick way: rake test
.
Contributing
I have only had the need to implement this with Postgres, ActiveRecord, and Sequel. I'm sure there is need for more databases like MySQL, and ORMs like Datamapper.