There are several reasons I like Rails’ fixtures for testing. One such reason is that modeling a small world gives you instant data for seeding your database.
If you always skip fixtures for factories when working with Rails, you are missing out!
What are fixtures? There are YAML definition of your database data which you can use in all your tests:
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
<% password = "XuRaT7tSp" %>
joe:
name: Joe
email: <%= Faker::Internet.unique.email %>
encrypted_password: <%= Devise::Encryptor.digest(User, password) %>
admin_clearance: 5
confirmed_at: <%= DateTime.now %>
chris:
name: Chris
email: <%= Faker::Internet.unique.email %>
encrypted_password: <%= Devise::Encryptor.digest(User, password) %>
admin_clearance: 4
confirmed_at: <%= DateTime.now %>
Once defined, they’ll always be there for you at your disposal:
# frozen_string_literal: true
require "test_helper"
class UserTest < ActiveSupport::TestCase
setup do
@owner = users(:joe)
@owners_team = teams(:owners)
...
end
test "#email_confirmed?" do
assert_not @owner.email_confirmed?
@owner.confirmed_at = DateTime.now
assert @owner.email_confirmed?
end
The main disadvantage of fixtures is indirection, the main advantage is speed. But there is more than the speed argument to it.
One small, but pretty neat thing is to reuse fixtures for your development data. Rails even have a task for it:
$ rails db:fixtures:load
This works automatically, but note that you must ensure that test dependencies used while defining fixtures now have to be moved to development group in your Gemfile
. This is the case of the Faker gem above.
You can also be more specific and load only the tables you want:
$ rails db:fixtures:load FIXTURES=users,teams
Now the obvious next step would be to combine with some custom seed data, perhaps a user you can log in with:
$ rails db:fixtures:load db:seed
If you always want to load your fixtures for seeding the database, I recommend to invoke the task from the db/seeds.rb
file:
# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).
#
# Examples:
#
# movies = Movie.create([{ name: "Star Wars" }, { name: "Lord of the Rings" }])
# Character.create(name: "Luke", movie: movies.first)
# Start with fixtures
Rake.application["db:fixtures:load"].invoke
# Add few more models...
User.create(...)
The task will work since Rails is loaded at this point.
And that’s it, really. One line of code for plenty of developer productivity.
Get Test Driving Rails and make your tests faster and easier to maintain.