Hiro
Hiro

Reputation: 359

DatabaseCleaner doesn't seem to clean between suits

saviors.

I'm having a trouble with cleaning database after each RSpec example. The thing is, that when I run rspec command, users_controller_spec.rb complains that there are more records than the example expects. Indeed the records are being created as it says if I check with rails c. when I run the this suite alone, it will be successful, so I assume it is because DatabaseCleaner doesn't clean the user records which other specs create(the number of user records matches the extra records users_controller_spec example claims to be). They are created in before :all block(if that matters).

Here is my rails_helper.rb

# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test'
require 'spec_helper'
require File.expand_path('../../config/environment', __FILE__)
require 'rspec/rails'

# Add additional requires below this line. Rails is not loaded until this point!
require 'devise'
require 'admin/v1/dashboard_controller'
# Requires supporting ruby files with custom matchers and macros, etc, in
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!

RSpec.configure do |config|
  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
  config.fixture_path = "#{::Rails.root}/spec/fixtures"

  config.include Devise::Test::ControllerHelpers, type: :controller
  config.include ControllerMacros, type: :controller
  # If you're not using ActiveRecord, or you'd prefer not to run each of your
  # examples within a transaction, remove the following line or assign false
  # instead of true.
  config.use_transactional_fixtures = true

  config.include FactoryGirl::Syntax::Methods

  config.infer_spec_type_from_file_location!

  config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
    DatabaseCleaner.clean_with(:truncation)
  end

  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end
end

users_controller.rb

describe 'GET #index' do
  it 'populates an array of users' do
    user1 = create(:user)
    user2 = create(:user)
    get :index
    expect(assigns(:users)).to match_array([user1, user2])
  end
  it 'renders :index template' do
    get :index, {}
    expect(response).to render_template :index
  end
end

UPDATE1: this is where the extra user records are created

    require 'rails_helper'

describe Admin::V1::MessagesController do
  let(:admin_user) do
    admin_user = double('admin_user')
    allow(request.env['warden']).to receive(:authenticate!).and_return(admin_user)
    allow(controller).to receive(:current_admin_v1_admin_user).and_return(admin_user)
    p '==='
  end
  before { login_admin_user admin_user }

  describe 'GET #index' do
    it 'renders :index template' do
      get :index, {}
      expect(response).to render_template :index
    end
  end

  describe 'GET #get_users' do
    before :all do
      @user1 = create(:user, nickname: 'hiro')
      @user2 = create(:user, nickname: 'elise')
    end
    context 'with params' do
      it 'populates an array of users matching on nickname' do
        get :get_users, format: :json, query: 'h'
        expect(assigns(:users)).to match_array([@user1])
      end
    end
    context 'without params' do
      it 'populates an array of all users' do
        get :get_users, format: :json
        expect(assigns(:users)).to match_array([@user1, @user2])
      end
    end
  end

  describe 'GET #get_messages' do
    before :all do
      @user1 = create(:user)
      @user2 = create(:user)
      @message1 = create(:message, user_id: @user1.id)
      @message2 = create(:message, user_id: @user1.id)
      @message3 = create(:message, user_id: @user2.id)
    end
    context 'with user_id' do
      it 'populates an array of messages with the user_id' do
        get :get_messages, format: :json, user_id: @user1.id
        expect(assigns(:messages)).to match_array([@message1, @message2])
      end
    end
  end
end

Upvotes: 3

Views: 874

Answers (1)

gmcnaughton
gmcnaughton

Reputation: 2293

Unfortunately RSpec's before(:all) does not play nicely with transactional tests. The code in before(:all) gets run before the transaction is opened, meaning any records created there will not be rolled back when the transaction is aborted. You are responsible for manually cleaning these items up in an after(:all).

See rspec-rails#496 and Using before(:all) in RSpec will cause you lots of trouble unless you know what you are doing

  after(:all) do
    # before/after(:all) is not transactional; see https://www.relishapp.com/rspec/rspec-rails/docs/transactions
    DatabaseCleaner.clean_with(:truncation)
  end

Upvotes: 5

Related Questions