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
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.
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.