Hiro Hiro - 1 month ago 7
Ruby Question

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

Answer

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
Comments