RSpec Data Leaks Between Examples - database_cleaner Strategy
Records created in one example survive into the next, making examples pass or fail depending on order. The database is not being reset between examples - usually a database_cleaner strategy that does not fit the spec type.
What this error means
Specs that pass alone fail in the full suite (or in a different random order) because data from an earlier example is still present. Counts are off, uniqueness validations trip, or a "should be empty" assertion finds stale rows.
Failure/Error: expect(User.count).to eq(1)
expected: 1
got: 4
# 3 users leaked in from earlier examplesCommon causes
No reset between examples
Without database_cleaner (or Rails transactional fixtures) wrapping each example, rows created in one example persist into the next.
Transaction strategy used for system/JS specs
Capybara/JS specs run the app in a separate thread/connection that cannot see the test’s open transaction. A transaction strategy leaves data the assertions cannot roll back - those specs need truncation.
How to fix it
Use transactions by default, truncation for JS specs
# spec/support/database_cleaner.rb
RSpec.configure do |config|
config.before(:suite) { DatabaseCleaner.clean_with(:truncation) }
config.before(:each) { DatabaseCleaner.strategy = :transaction }
config.before(:each, type: :system) { DatabaseCleaner.strategy = :truncation }
config.around(:each) { |ex| DatabaseCleaner.cleaning { ex.run } }
endSurface the leak with random ordering
- Run with
--order randomto expose order-dependent pollution. - Ensure
use_transactional_fixtures = falsewhen database_cleaner manages cleanup. - For multi-connection (JS) specs, use truncation so all connections see a clean DB.
How to prevent it
- Wrap every example in database_cleaner (or transactional fixtures).
- Use truncation for system/JS specs that span connections.
- Run specs in random order in CI to catch leakage early.