Rspec/FactoryGirl: clean database state

2020-03-09 07:43发布

问题:

I am new to Rspec and Factory girl and would like my test to run on a specific database state. I understand I can get Factory girl to create these records, and the objects will be destroyed after the test run, but what happens if I have data in the database.

For example: I want my test to run when there are 3 records in the database that I created through Factory Girl. However, I currently already have 1 model record in the database, and I don't want to delete it just for the test. Having that 1 model in there ruins my test.

Database Content

[#<Leaderboard id: 1, score: 500, name: "Trudy">]

leaderboard_spec.rb

require 'spec_helper'

describe Rom::Leaderboard do

    describe "poll leaderboard" do
        it "should say 'Successful Run' when it returns" do
            FactoryGirl.create(:leaderboard, score: 400, name: "Alice")
            FactoryGirl.create(:leaderboard, score: 300, name: "Bob")
            FactoryGirl.create(:leaderboard, score: 200, name: "John")
            Leaderboard.highest_scorer.name.should == "Alice"
        end
    end

end

Now my test will fail because it will incorrectly assume that Trudy is the highest scorer, since the test have run in an incorrect state.

Does factory girl offer anyway to delete records from the database then rollback this delete? Similar to how it creates records in the database and rollsback

回答1:

Its popular to use the database_cleaner gem. You can find it here:

https://github.com/bmabey/database_cleaner

The documentation recommends the following configuration for rspec:

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

This will you make sure you have a clean database for each test.



回答2:

To answer your rollback question as directly as possible: no there isn't a way to rollback a delete within a test.

Following test conventions your goal usually is to start with a clean slate, and use factory_girl to efficiently build the scenario in the database you need to test for.

You can accomplish what you want by, for instance, adding a this to your leaderboards.rb factory file:

factory :trudy do
  id 1
  score 500
  name "Trudy"
end

Or you could create a simple helper function in your test file that regenerates that record when its needed for tests:

def create_trudy
  FactoryGirl.create :leaderboard,
    id: 1,
    score: 500,
    name: "Trudy"
  end
end

Or you could place all this in a before(:suite) within a describe block, like so:

describe "with a leaderboard record existing" do
  before(:each) do
    FactoryGirl.create :leaderboard, id: 1, score: 500, name: "Trudy"
  end
  # Tests with an initial leaderboard record
end
describe "with no leaderboard records initially" do
  # Your test above would go here
end

In this final suggestion, your tests become very descriptive and when viewing the output, you will know exactly what state your database was in at the beginning of each test.