DatabaseCleaner / database_cleaner

Strategies for cleaning databases in Ruby. Can be used to ensure a clean state for testing.
https://www.rubydoc.info/github/DatabaseCleaner/database_cleaner
MIT License
2.94k stars 484 forks source link

Database not properly cleaned #123

Closed conradwt closed 9 years ago

conradwt commented 12 years ago

The database, MySQL, isn't being properly cleaned using the RSpec recipe below:

RSpec.configure do |config|

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

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end

end

Setup:

OS:                             Mac OS 10.7.4

Ruby:                         1.9.2p323

Rails:                          3.2.6

DatabaseCleaner:   0.8.0

mysql2:                      0.3.11

MySQL:                     5.1.62

rspec:                         2.10.0

rspec-rails:                2.10.1

Lastly, this isn't so much of an issue with the unit tests but causes issues with the controller and acceptance tests.

terrafied commented 12 years ago

+1 to this. Almost exactly the same setup. No idea why it's not, can't track it down.

dpmccabe commented 12 years ago

I'm having this same problem with the same setup (except with Postgres). One model in particular is not getting cleaned. I can tell because if I raise Subject.all.inspect inside one of my rspec tests, it returns results when it shouldn't.

bmabey commented 12 years ago

Very odd. My only guess is that the table doesn't exist when DatabaseCleaner queries for the existing tables. Could someone reproduce the bug in a sample rails app?

bmabey commented 12 years ago

What are the names of the tables not being cleaned? Maybe that would provide a clue...

conradwt commented 12 years ago

@bmabey In my case, the table name is jobs. Also, it doesn't effect the running specs but it's something that I noticed by looking at the contents of the database after they completed.

dpmccabe commented 12 years ago

I was wrong. I've determined that it's actually not a particular table that isn't getting cleaned, but rather all tables after one particular model spec runs. If I ignore that spec from my suite, the problem doesn't occur.

Here is my spec_helper.rb: https://gist.github.com/3279090

I'm not using transactional_fixtures and I think I have everything set up properly, but for some reason after my suite runs, there's still a bunch of stuff left in my test database.

I even put this in the problem spec:

  after(:all) do
    puts Subject.all.count
    DatabaseCleaner.clean
    puts Subject.all.count
  end

It outputs 7 both times.

bmabey commented 12 years ago

If you tail the log for ActiveRecord or your DB do you see TRUNCATE commands being issued?

bmabey commented 12 years ago

Add a puts DatabaseCleaner.connections.inspect and paste what it outputs...

dpmccabe commented 12 years ago

Hmm, I only see a truncate command in test.log before the suite runs: https://gist.github.com/3279394 (snipped at end)

I inspected the DC connections in my after(:all) block and got [#<DatabaseCleaner::Base:0x000001038983d0 @autodetected=true, @orm=:active_record, @strategy=#<DatabaseCleaner::ActiveRecord::Transaction:0x00000103a607f8 @db=:default>>].

dpmccabe commented 12 years ago

It's possible that DatabaseCleaner is never running after each test, even though I've specified it in my spec_helper.rb above. If I exclude what I thought was the problematic spec and run my suite, User.all, for example, still returns entries in my Rails console's test environment.

If I destroy_all on the Users, run the test suite again, they're back.

Theoretically, with DatabaseCleaner, this shouldn't be happening, right?

bmabey commented 12 years ago

Oh right, you are using :transaction so you shouldn't be seeing TRUNCATE but seeing the transactions being rolled back. Try using the (slower) :deletion strategy temporarily to see if that cleans it.

conradwt commented 12 years ago

@bmabey I have removed all the data from table in question and ran the specs. Thus, the test database after running the specs should be empty if I'm using the transaction strategy. Is this correct in this scenario given the preconditions?

bmabey commented 12 years ago

Yes, that is correct. Assuming that you aren't populating the table outside of a spec and your app isn't prematurely committing the transaction. That's why I suggested using another strategy like :deletion to help debug.

On Aug 6, 2012, at 5:57 PM, Conrad Taylor notifications@github.com wrote:

@bmabey I have removed all the data from table in question and ran the specs. Thus, the test database after running the specs should be empty if I'm using the transaction strategy. Is this correct in this scenario given the preconditions?

— Reply to this email directly or view it on GitHub.

dpmccabe commented 12 years ago

Your latest comment just helped me figure it out. I remembered that I am, in fact, manually committing a transaction in a Class I am testing (e.g. MyModel.connection.commit_db_transaction). I changed to the deletion strategy, which is a bit slower, while I investigate whether there's a way to avoid having to manually commit a transaction.

bmabey commented 12 years ago

I would suggest sticking with :transaction for the suite and then tagging certain specs that need to be cleaned via another strategy.

lazylester commented 11 years ago

It seems that the MyISAM database engine doesn't support rollback. (http://lists.mysql.com/mysql/201528). So transaction strategy doesn't work in that case. When I created the db, my server was configured for MyISAM (for reasons I don't know), and this caused this fine and useful gem to fail. There is a mySQL warning available when rollback fails, and it would be a good idea to propagate this up into ruby-land somehow, as otherwise the failure is silent.

conradwt commented 11 years ago

@lazylester I'll test things against PostgreSQL now that I'm doing mostly all client projects with this database engine.

railyboy commented 11 years ago

I have the same problem when using Spork, RSpec (rails 2.12), mongoid, and Capybara (2.0.2).

It took a bit of googling and trying various solutions. Here is the one that worked for me in my spec_helper.rb file.

require 'rubygems'
require 'spork'
#uncomment the following line to use spork with the debugger
#require 'spork/ext/ruby-debug'

Spork.prefork do
  # Loading more in this block will cause your tests to run faster. However,
  # if you change any configuration or code from libraries loaded here, you'll
  # need to restart spork for it take effect.

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

  # Requires supporting ruby files with custom matchers and macros, etc,
  # in spec/support/ and its subdirectories.
  Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

  RSpec.configure do |config|
    # ## Mock Framework
    #
    # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
    #
    # config.mock_with :mocha
    # config.mock_with :flexmock
    # config.mock_with :rr

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

    # 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

    # Clean up the database

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

      DatabaseCleaner.orm = "mongoid"
    end
    config.before(:each) do
      DatabaseCleaner.strategy = :truncation
      DatabaseCleaner.clean
    end
    config.after(:each) do
      DatabaseCleaner.clean
    end

    # If true, the base class of anonymous controllers will be inferred
    # automatically. This will be the default behavior in future versions of
    # rspec-rails.
    config.infer_base_class_for_anonymous_controllers = false

    # Run specs in random order to surface order dependencies. If you find an
    # order dependency and want to debug it, you can fix the order by providing
    # the seed, which is printed after each run.
    #     --seed 1234
    config.order = "random"
  end

end

Spork.each_run do
  # This code will be run each time you run your specs.
  ActiveSupport::Dependencies.clear 
end

In summary I added the following in the before(:each) block, as the commands from the before(:suite) block seemed to be ignored

***DatabaseCleaner.strategy = :truncation***

Being newish to using this, not sure if its correct, but I've gained a lot of help from the community, so thought I would give back with what worked for me.

fabianoalmeida commented 11 years ago

I'm having the same problem. My database is SQLite3 and my spec config is:

# spec/spec_helper.rb

require 'rspec/rails'
require 'rspec/autorun'
require 'capybara/rspec'
require 'database_cleaner'

Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

RSpec.configure do |config|
  config.include Devise::TestHelpers, :type => :controller

  config.fixture_path = "#{::Rails.root}/spec/fixtures"

  config.use_transactional_fixtures = false

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

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end

  config.order = "random"
end

And in my models spec:

# spec/models/task_spec.rb

require 'spec_helper'

describe Task do

  subject(:task) { FactoryGirl.build(:task) }

  # ...

  it "should not update if #active field is false" do
    updated_at = task.updated_at
    task.active= false
    task.destroy
    expect(task.updated_at).to eq updated_at
    expect(task.active).to be_false
    expect(Task.actives.count).to have(:no).items
  end
end

When I'm testing how many registers exist in my database expect(Task.actives.count).to have(:no).items, it's returning to me the number of test in my file. I have eight tests, so .count is returning this value.

Thoughts? :confused:

sgonyea commented 11 years ago

This seems to be affecting me, too. Transactional Fixtures had been enabled... But I disabled it for some Capybara tests. In the specs where DatabaseCleaner should be setting up transactions, the Rails log instead looks like:

 (0.4ms)  SAVEPOINT active_record_1
SQL (0.5ms)  INSERT INTO `settings` (<SNIP>) VALUES (<SNIP>)
 (0.3ms)  RELEASE SAVEPOINT active_record_1
 (0.3ms)  SAVEPOINT active_record_1
SQL (0.5ms)  INSERT INTO `settings` (<SNIP>) VALUES (<SNIP>)
 (0.2ms)  RELEASE SAVEPOINT active_record_1
 (0.2ms)  SAVEPOINT active_record_1
SQL (0.5ms)  INSERT INTO `settings` (<SNIP>) VALUES (<SNIP>)
 (0.3ms)  RELEASE SAVEPOINT active_record_1

The fixtures are created using FactoryGirl. I'm not sure what I should be seeing, but when I'm in a Pry session in the middle of my spec and I create a record -- it continues to exist after the spec has completed. I don't see any SAVEPOINT information being inserted, etc. Something is definitely borked.

abatko commented 10 years ago

I believe I am experiencing this as well. In my case it seems that in a feature test (integration test) all but one table have been cleaned (emptied).

Here is what I have in test/test_helper.rb:

require 'database_cleaner'
DatabaseCleaner.strategy = :transaction

class MiniTest::Unit::TestCase
        def setup
                DatabaseCleaner.start
        end

        def teardown
                DatabaseCleaner.clean
        end
end

For the time being I added the following to the integration test to clean the affected table:

        def setup
                Event.delete_all
        end

Note: The fixtures are created using FactoryGirl.

conradwt commented 10 years ago

@abatko I tend to do all cleanup within the teardown method. However, the setup works as well but it will leave records in the database after the last test. These days I tend to move away from the setup and teardown methods because I really like having self contained integration and unit tests. You're doing the right thing because it allows you to move forward within your development.

abatko commented 10 years ago

I am going out on a limb to say that after some painstaking debugging I seem to have resolved this issue for myself. As I mentioned earlier, I am using FactoryGirl, and it turns out that I was incorrectly creating/assigning attributes dynamically (aka lazy attributes).

Wrong:

    event_id FactoryGirl.create(:event).id

Correct:

    event_id { FactoryGirl.create(:event).id }

After making this change I have not been able to reproduce the error, and this also means that I removed setup / delete_all.

conradwt commented 10 years ago

@abatko Is this code within your factory file? If this is the case, then you should be able to simply do the following:

FactoryGirl.define do
    factory :some_object do
        attribute1 "attribute1"  
        attribute2 "attribute2"
        event
    end

    factory :event do
        attribute3 "attribute3"  
        attribute4 "attribute4"
    end
end

Now, you can do the following within your test code:

object = FactoryGirl.create(:some_object)
event_id = object.event.id
tborisova commented 10 years ago

§

etagwerker commented 9 years ago

Is anyone still experiencing this problem? I'd like to close it, because it's too broad and I'd prefer it if you opened new issues that are more specific (ie. ORM + DBMS + strategy)

If you're still having a similar problem, please open a new issue specifying ORM + DBMS + strategy (optional)

Thanks!

alecslupu commented 9 years ago

I have encountered the problem myself in a Rails 3.2 + Rspec 3 environment. Changing my rails_helper.rb to :

config.use_transactional_fixtures = false

solved my problems. A

DemitryT commented 9 years ago

I was running into a similar issue, but eventually got it to work with the truncation strategy. My issue ended up being that I was using ActiveRecord to create some of the objects I was working with in my tests when I should have been using factories I had setup with FactoryGirl. Hope that helps someone.

ka8725 commented 2 years ago

I had a similar case: some user records were not cleaned from DB. My analysis showed that those records that assigned to Thread.current are not cleaned automatically by DatabaseCleaner.

In the end, I come up with the following fix:

  config.after(:each) do
    # Thread.current[:current_user] is set in some tests
    # it should be cleaned before database cleaner runs, otherwise it won't be dropped from DB.
    Thread.current[:current_user] = nil
    DatabaseCleaner.clean
    puts User.count # this output along with running "rspec --format d" can show the places causing issues in the app
  end