This blog post is part of a series on testing. Read Part 1 here.
Good things always come in pairs, and that couldn’t be more true when it comes to testing. Like milk and cookies or peanut butter and jelly, test suites and test data are at their best when they’re put together. As we discovered last week, a thoughtful test suite is important when it comes to checking our assumptions. But even the most comprehensive test suite is nothing without the appropriate amount – and type – of test data to support it.
Every Rails application comes with a production, development, and test environment, and good test data is an indication of a well-constructed testing environment. But not all data is created equally. To take a cue from George Orwell, we could go so far as to claim that some forms of test data are more equal than others.
Enter FactoryGirl, a gem that I’ve recently discovered to be the most efficient way and painless way of creating test data within a Rails application. Generating test data is often the culprit for not only a great deal of pain and sufferring, but also some annoying bugs that are hard to catch. In fact, one of the most excruciating bugs I’ve ever dealt with came from a single line of code, meant to create some test data. So it seems fitting that this week we tackle the most dangerous (yet thrilling!) part of testing: generating test data.
Setting Up The Factory Floor
Since we’re implementing FactoryGirl
on top of a Rails application, we’ll work exclusively with the factory_girl_rails
gem, which has been built specifically for this purpose.
Once we’ve added gem 'factory_girl_rails'
to our Gemfile
and run a bundle install
in the terminal, we’re ready to start setting up our factories. We’ll start by creating factories for our ReadingList
model from last week’s post.
Within our
/spec
directory, we’ll need to create afactories
directory. This is where each of the factories for each model will live.Inside of our
/spec/factories/
subdirectory, we’ll create a file for ourReadingList
factory. The convention for naming factories is to use the plural form of the model name in snake case, with the word “factory” appended to the end. In our case, that file would be namedreading_lists_factory.rb
.In our newly-created
ReadingList
Factory, we’ll add a block that defines what ourFactoryGirl
object will look like. Every single factory you make for any instance of test data will begin with a block exactly like this one:
1 2 |
|
- Next, we’ll want to give this test object a name. For this very basic test suite, we only want to test one
ReadingList
object, so we’ll stick with a simple name:reading_list
. Keep in mind that whatever name we give this test object is what we’ll be using to refer to it inside of our tests. Inside of our initialFactoryGirl
block, we’ll define this specific object like this:
1 2 3 4 |
|
- Lastly, we need to define some attributes for our test object. For now, our
ReadingList
test object needs only three columns in the database: one for itsid
, one for itstitle
, and a foreign key of theUser
it belongs to. Here’s what our final test object definition looks like:
1 2 3 4 5 6 |
|
Pretty cool, right? We don’t have to give our test object an id
, because FactoryGirl
will generate one when we call on it to create a new test object. So what’s that user
line doing in there? Well, it’s creating an association between two factories! When FactoryGirl
sees user
, it looks for a file with the path /spec/factories/user_factory.rb
, and creates an instance of a User
test object, which it then uses to build our ReadingList
object.
But right now, our code will give us a big, nasty error. Can you guess the reason behind that? Why, we don’t have a User
factory, of course! I guess we better get on that.
Form Follows Function
Now that we know how to set up our factories pretty quickly, we can hop on making a users_factory
. For now, our User
objects have only a first_name
and a last_name
attribute, so we’ll create a User
test object that satisfies these requirements.
But, some Users
can also be admin
s, but this is an optional trait that not all User
s will have. This is something we definitely want to test, but we also want to keep our code DRY. How do we handle this? By nesting factories!
1 2 3 4 5 6 7 8 9 10 |
|
This tells FactoryGirl to define two different user
instances: a user
test object, and an admin
test object, which inherits the traits of the user
object – namely, its first_name
and last_name
attributes.
While we’re at it, why don’t we set up our books_factory
as well? That might look something a little like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Wait, what’s up with the class: Book
syntax? We haven’t seen that yet! Well, not to worry – you only have to use it in a specific situation! What exactly is that situation, you might ask? Well, the only time you ever need to explicitly define a FactoryGirl object’s class is if the name of the object – book1
or book2
in our case – is different from the object’s class name. If we had only a single book
test object, we wouldn’t have to define the class name, since FactoryGirl will know to look for it in the books_factory.rb
file.
Remember last week when we had to manually create two different Book
objects every single time we wanted to create a new test object? Well, here we’re defining two test objects in a singular, isolated place.
Awesome! Now it’s time to bring it all together in our cleaned-up test suite!
Bypassing Testing Bugs
When we wrote out the first iteration of our test suite last week, we had a few different let
blocks, inside of which we called create!
and build
on our different objects. Our let
blocks definintely helped us tidy up our tests, since we built and created all of our test objects at once, at the very top of our do
block.
But as is the case with most of programming, there’s a better way to do that. Now that we’ve implemented FactoryGirl, we can cut out some of those blocks, and only call them when we need them. And, we don’t need to create an instance with the attributes explicitly defined in a block – instead, we just tell FactoryGirl to create it for us!
Here’s a refactored version of last week’s tests, now with the factory_girl_rails
to help us out:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
You’ll notice that I’ve replaced build
with create
in this iteration of tests. I initially had used build
, but when I started implementing FactoryGirl, I hit a wall. In fact, I spent an unmentionable number of hours trying to figure out what on earth I was doing wrong!
It turns out I was using the wrong tool for the job. The build
method creates an instance, but does not save it. The create
method, on the other hand, both creates and persists the object. This was one of the most important debugging lesson I learned while writing tests, and it was not a fun lesson to learn. I hope that at the very least, you won’t have to struggle through that bug like I did!
The thing that I valued the most during this debugging process was learning how FactoryGirl
actualy works. If those build
and create
methods seem an awful lot like ActiveRecord to you, that’s because it IS ActiveRecord!
In fact, when we call create(:book1)
, the magic of FactoryGirl
actually does the following:
- Creates a new
ReadingList
. - Saves the
ReadingList
. - Creates a new
Book
. - Associates that
Book
with theReadingList
. - Saves the
Book
.
Amazing, right? We get so much functionality, all in a single method call! Learning this made me appreciate what FactoryGirl
does so much more than if I had just included it blindly in my Gemfile
without giving it a second though.
Thoughtbot, the creators of the factory_girl
gem, has a great post explaining how it interacts with ActiveRecord. I found their explaination super helpful when I was first learning about FactoryGirl:
When you invoke a factory, factory_girl uses your definitions to compile a list of attributes that should be assigned to that instance, as well as any associated factories. It saves associations first so that foreign keys will be properly set on dependent models. To create an instance, it calls new without any arguments, assigns each attribute (including associations), and then calls save!. factory_girl doesn’t do anything special to create ActiveRecord instances. It doesn’t interact with the database or extend ActiveRecord or your models in any way.
Sure, when it comes to the world of testing, the night may be dark and full of terrors. But with FactoryGirl
on your side, you’ll feel safer, as though you’ve got an army of dragons to back you up. In my head, they’re super cute ones kinda like these:
Tune in again next week, when I’ll cap off this series by sharing two more gems we can add to our army of testing dragons: shoulda-matchers
for writing quick and easy validations, and database_cleaner
, the key to unlocking your dreams of a neat and tidy testing database. Until then, test on, my friends – test on!
tl;dr?
- The
factory_girl_rails
gem is used to generate test data for a Rails application, and each factory defines the attributes and associations of a test object. All factory files should be created in the./spec/factories
subdirectory. - This post only covers a couple of the tricks that
FactoryGirl
has up her sleeve. To read them all, check out the gem’s extensive documentation. - This tutorial is super detailed and I referred to it frequently while writing this blog post. If you want to learn more about testing and implementing
FactoryGirl
, give it a read. - Did you know that factories and fixtures are actually quite different? No? Well then, you should read this post and get all caught up.