Fixtures in RSpec
Solution 1
If you want to use fixtures with RSpec, specify your fixtures in the describe block, not within a before block:
describe StudentsController do
fixtures :students
before do
# more test setup
end
end
Your student fixtures will get loaded into the students table and then rolled back at the end of each test using database transactions.
Solution 2
First of all: You cannot use method fixtures
in :all
/ :context
/ :suite hook
. Do not try to use fixtures in these hooks (like post(:my_post)
).
You can prepare fixtures only in describe/context block as Infuse write earlier.
Call
fixtures :students, :teachers
do not load any data into DB! Just prepares helper methods students
and teachers
.
Demanded records are loaded lazily in the moment when You first try to access them. Right before
dan=students(:dan)
This will load students and teachers in delete all from table + insert fixtures
way.
So if you prepare some students in before(:context) hook, they will be gone now!!
Insert of records is done just once in test suite.
Records from fixtures are not deleted at the end of test suite. They are deleted and re-inserted on next test suite run.
example:
#students.yml
dan:
name: Dan
paul:
name: Paul
#teachers.yml
snape:
name: Severus
describe Student do
fixtures :students, :teachers
before(:context) do
@james=Student.create!(name: "James")
end
it "have name" do
expect(Student.find(@james.id)).to be_present
expect(Student.count).to eq 1
expect(Teacher.count).to eq 0
students(:dan)
expect(Student.find_by_name(@james.name)).to be_blank
expect(Student.count).to eq 2
expect(Teacher.count).to eq 1
end
end
#but when fixtures are in DB (after first call), all works as expected (by me)
describe Teacher do
fixtures :teachers # was loaded in previous tests
before(:context) do
@james=Student.create!(name: "James")
@thomas=Teacher.create!(name: "Thomas")
end
it "have name" do
expect(Teacher.find(@thomas.id)).to be_present
expect(Student.count).to eq 3 # :dan, :paul, @james
expect(Teacher.count).to eq 2 # :snape, @thomas
students(:dan)
expect(Teacher.find_by_name(@thomas.name)).to be_present
expect(Student.count).to eq 3
expect(Teacher.count).to eq 2
end
end
All expectations in tests above will pass
If these test are run again (in next suite) and in this order, than expectation
expect(Student.count).to eq 1
will be NOT met! There will be 3 students (:dan, :paul and fresh new @james). All of them will be deleted before students(:dan)
and only :paul and :dan will be inserted again.
Solution 3
before(:all)
keeps the exact data around, as it's loaded/created once. You do your thing, and at the end of the test it stays. That's why bui's link has after(:all)
to destroy or use before(:each); @var.reload!;end
to get the latest data from the tests before. I can see using this approach in nested rspec describe blocks.
Kris
Updated on July 12, 2022Comments
-
Kris almost 2 years
I'm new to using RSpec for writing tests in a Rails application which uses a MySQL database. I have defined my fixtures and am loading them in my spec as follows:
before(:all) do fixtures :student end
Does this declaration save the data defined in my fixtures in the students table or does it just load the data in the table while the tests are running and remove it from the table after all the tests are run?
-
nruth over 9 years
-
Foton over 7 yearsYeah! I found the trick to make all fixtures to be loaded before all tests. Just add RSpec.configure { |config| config.global_fixtures= :all } AND test in spec_helper directly, which will try to access any fixture. This way all fixtures are loaded in advance.