More interesting methods to get the most out of FactoryGirl
A while back we published a post on FactoryGirl that covered traits
, create_pair
and create_list
. One of the comments mentioned build_stubbed
and build_stubbed_list
, so we have done some research about the different strategies FactoryGirl offers, below you’ll read about those and when and where to use them in your tests.
create
This would be our standard/everyday strategy. Given a factories/classrooms.rb
file:
factory :classroom do |f|
sequence(:name) { |n| "Class #{n}}" }
active true
teacher
end
When we invoke create(:classroom)
, we are actually creating a Classroom in our database, that is validated and saved, along with its associations (teacher, in this case).
This strategy is the ideal if we want to test our before or after hooks work in our Classroom model, for example, or if we need to test a service that acts on our classrooms depending on their database status.
build
This strategy would be equivalent to invoking new
on our Classroom. It will fill all the fields on the model, but won’t run save
. As it doesn’t touch the database, it’s significantly faster than create
.
A good use for this strategy would be to test custom validations on your model specs:
@teacher = build(:teacher, mobile_number: "98352462831")
expect(@teacher.valid?).to eq(false)
expect(@teacher.errors.messages[:mobile]).to equal(["can't be a land line"])
Right now saving a hit on the database might not seem as much, but think of all the validations you’ll have to run for all the models in your app. When your application grows, you’ll be happy with having scraped all the milliseconds off you could along the way.
We have to be careful about associations, though. Looking at our factory file for Classroom at the top, when we use the Classroom factory, we are invoking the teacher
factory, and unless we explicitly specify it, that call will use the default strategy, create
. This way, if we do:
@classroom = build(:classroom)
It will create a teacher, then associate it to our built classroom. This means we would be still hitting the database. A good possibility would be to define an alternate factory:
factory :built_classroom do |f|
sequence(:name) { |n| "Class #{n}}" }
active true
association :teacher, factory: :teacher, strategy: :build
end
Also, have in mind that the before(:create)
and after(:create)
callbacks in your traits might need some revision and you might need to add an after(:build)
callback.
Finally, as with create
, we have build_pair
and build_list
available.
build_stubbed
This one is similar to build
, but it spices our built object up by giving it an id
and stubbing its methods, so the object acts as if it was persisted.
> user_a = FactoryGirl.build_stubbed(:user)
=> #<User id: 1001, email: "[email protected]", encrypted_password: "$2a$10$RYZFsx.KCyOS/CMjsmZxOeXugs14HIdMJ46eDTlVMPy...", ... >
> user_a.persisted?
=> true
> user_b = FactoryGirl.build(:user)
=> #<User id: nil, email: "[email protected]", encrypted_password: "$2a$10$rTTE9izfIWFtVdNhvOg6q.jNoRWSfWZ4TUFJMLg9yVF...", ... >
> user_b.persisted?
=> false
Also, it will throw an error when we try to write to the database with a stubbed model
> user_a.activate! # this method updates a field on the model
RuntimeError: stubbed models are not allowed to access the database
So, build_stubbed
is useful when we want to test our model methods if they don’t commit to the database.
Again, keep in mind what I explained before about the associations and callbacks (this time it would be after(:stub)
), as they applies in this case also. Then again, we have build_stubbed_list
and build_stubbed_pair
available to use.
attributes_for
I’d say that this one, more than a strategy, is an utility. It will give you a hash with the attributes of the model filled up as per the factory instructions, leaving out associations, id and timestamps. It’s the kind of hash you would love to use on your controller specs to test the creation of users, for example.
> FactoryGirl.attributes_for(:user)
=> {:email=>"[email protected]", :password=>"12345678", :password_confirmation=>"12345678", :first_name=>"Linnea", :last_name=>"Nitzsche", :mobile_phone_number=>"398.862.7690 x155", :wants_newsletter=>true}
Bonus Track: The Faker gem
In the attributes_for
example above, you can see that data is “realistic”. To get that kind of data instead of just plain sequences in the names and other fields, you can use the Faker gem. It’s zero-configuration and it will produce realistic data for you out of the box for an en-US locale, but it allows you to select your locale to produce data adapted to your needs. It’s useful not only for tests, but also to seed data on a demo app!
Conclusion
In this post we’ve reviewed the four build strategies given by FactoryGirl. Once again, create
will give you all you need, so it’s ok to use it. But keep in mind that apps will grow, and with them their test suites, so, learn about the other build strategies, and experiment with them. When you hit the 3000 specs mark, you’ll be glad you don’t have to wait 15 minutes to get the whole test suite passed.
Picture by Daniel Foster at Flickr