There is life beyond FactoryGirl.create()
If you are used to testing with Rails, chances are you already know FactoryGirl, the de facto standard substitute for fixtures, and you are used to use something like this on your tests:
FactoryGirl.create(:classroom)
…relying on your Classroom factory:
factory :classroom do |f|
sequence(:name) { |n| "Class #{n}}" }
active true
teacher
end
Well in this post I’ll show you some advanced uses of FactoryGirl to go a bit beyond the basics, tips & tricks you’ll love and will be eager to experiment with and use on your next projects.
Don’t repeat yourself
Having to type FactoryGirl.
seems a little thing, but if you count how many times you do it in the development of a project, that’s A LOT. So, the first thing would be mixin FactoryGirl methods in your spec helper:
# File spec/spec_helper.rb
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
end
If you are using other than RSpec, check the docs to find your equivalent.
Now that we are properly set up, let’s continue…
I heard you like traits…
Traits are a very nice way of having at hand ways to create objects with additional certain properties, let’s say we need to use a classroom with students on it on our tests, or a classroom with a support_teacher:
factory :classroom do |f|
sequence(:title) { |n| "Class #{n}}" }
active true
teacher
trait :with_students do
after(:build) do |classroom|
create(:student, classroom: classroom)
end
end
trait :with_support_teacher do
after(:create) do |classroom|
classroom.support_teacher = create(:teacher)
end
end
end
Then if we wanted to use in our tests an article with comments that is also on frontpage, we could do:
create(:classroom, :with_students, :with_support_teacher)
Or going a bit further we can create a trait that uses traits itself:
trait :full_class do
with_students
with_support_teacher
end
And then beautifully do:
create(:classroom, :full_class)
But I need them in large numbers!
Our previous trait with_students
is not really faithful to its name, but it would be very easy to correct with the create_pair
method:
trait :with_students do
after(:build) do |classroom|
create_pair(:student, classroom: classroom)
end
end
That will give you two students in our classroom. But let’s say we want more, a very populated classroom full of noisy children:
trait :with_students do
after(:build) do |classroom|
create_list(:student, 25, classroom: classroom)
end
end
Even more, what if we wanted to be able to set the number of students each time we use the factory, instead of having a prefixed amount? That’s when ignored attributes and the evaluator come in handy.
Ignored attributes are not used on factory creation (i.e.: not set on the created object), and are handy to store or pass data we want to use in callbacks or traits, via the evaluator, this way:
trait :with_students do
ignore do
number_of_students 10
end
after(:build) do |classroom, evaluator|
create_list(:student, evaluator.number_of_students, classroom: classroom)
end
end
In short, ignored attributes would set our default values, but allow us to set them as we wish, so these two are valid:
create(:classroom, :with_students) # would still give us 10 students in the classroom
create(:classroom, :with_students, number_of_students: 7)
Photo by Biczzz on Flickr