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:

  1. DatabaseCleaner owns the cleaning and acts as we want it to.
  2. We get to add feature specs and increase coverage.
  3. Our test suite speed is only minimally impacted since we use :transaction cleaning and transactional_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.