A Happy RSpec, Capybara and DatabaseCleaner Setup
There have been a few times where I ran into problems with RSpec Capybara feature specs and DatabaseCleaner
. Spec issues arise because most Capybara JavaScript drivers run your specs on a different thread than the app. This Stack Overflow Answer provides a great explanation.
Now, the accepted answer the SO answer of simply turning off transactional_fixtures
in RSpec and using the :truncation
cleaning strategy in before/after hooks will work. However, when working in a large app (20+ minutes for full specs) this will slow your spec suite down dramatically. Why? From the DatabaseCleaner [documentation](DatabaseCleaner on which cleaning strategy is fastest:
For the SQL libraries the fastest option will be to use :transaction as transactions are simply rolled back.
After some research and lots of head banging, below is the configuration I have finally come to be happy with that gives us the best of all three worlds:
- DatabaseCleaner owns the cleaning and acts as we want it to.
- We get to add feature specs and increase coverage.
- Our test suite speed is only minimally impacted since we use
:transaction
cleaning andtransactional_fixtures
everywhere except our feature specs.
# spec/spec_helper.rb
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, js: true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
config.before(:context, js: true) do
self.use_transactional_fixtures = false
end
config.after(:context, js: true) do
DatabaseCleaner.clean_with(:truncation)
self.use_transactional_fixtures = true
end
end
How this works
Simply tag your feature specs with a js: true
block and everything will just work:
# spec/features/my_feature_spec.rb
RSpec.describe "my feature", js: true do
# do the testing stuff
end
As mentioned before, this js: true
tag will disable RSpec's use of transactional fixtures and set the cleaning strategy to :truncation
.
One final note: this configuration will get more complicated if you use other test dependencies or configurations. Typically, you'll want to make sure those are loaded after DatabaseCleaner
is configured.