Feb 17, 2016
Table of contents:
When working on a large application, it’s very important that you are confident in your tests.
As a single developer you won’t be able to hold the entire codebase in your head, and when working with a team, you need to ensure your tests can be easily understood by your colleagues.
An important part of writing tests is creating the situation so your test passes correctly. For example, a lot of types tests will require certain data in the database in order for those conditions to be met.
Rails ships with Fixtures that allow you to automatically populate the database with data before each test.
However, Fixtures add a certain level of ambiguity to your tests and can make it difficult to write clean tests once your application reaches a certain size.
An alternative is to use Factories. A Factory is a way to create valid model objects on demand so you can write tests that require a certain situation.
One of the most popular gems for creating Factories is Factory Girl. In today’s tutorial we will be looking at using Factory Girl in Ruby on Rails.
Up until this point I’ve been using Fixtures in these tutorials and every thing has been fine, so you’re probably thinking, “what’s the problem with Fixtures?”.
I think there are two main problems with using Fixtures.
Firstly, before each test is run, your Fixtures will be executed so that the data is in the database.
This is fine when you are first starting out with your application because you will only have a handful of Fixtures and and so it will be manageable.
However, once you have been developing your application for a while you will probably face the situation where you have created lots of different fixtures for all of the scenarios you want to test with.
Once you have a lot of different fixtures it becomes increasingly difficult to get a good idea of the “state of play” of the application during any given test.
For example, you might write a test that should pass under a given circumstance, but it’s actually passing for a different reason.
Or perhaps you write a test and expect it to fail, but it actually passes because of your Fixture data.
In either case, I don’t like the fact that the environment for the test under execution is not completely controlled.
Secondly, because the data is automatically inserted before each test it’s not always obvious what is going on in a test.
For example, if you are asserting that saving a model fails because of a uniqueness validation rule, it won’t be clear from just reading the test that a record already exists.
Factory Girl is different in a couple of ways.
Firstly, instead of defining each Fixture, you define a Factory that can be used to create models of that type. The Factory is basically just a blueprint for creating instances of that model object.
Now whenever you need an instance of the model, or you need to pre-populate the database with data for a given scenario, you can use the Factory.
This means that only the data required for the test will be in the database and it’s immediately clear where that data came from.
So hopefully that explains why I prefer Factory Girl over regular Fixtures.
Factory Girl is a Ruby gem and so we can add it to the project by listing it in the GemFile
:
group :test do
gem 'factory_girl_rails'
end
Once you have updated your GemFile
, run the following command in Terminal:
bundle install
I’m using Mini Test as my testing framework. To add Factory Girl to Mini Test, add the following to your test_helper.rb
file under the test
directory:
class ActiveSupport::TestCase
include FactoryGirl::Syntax::Methods
end
If you are using RSpec, this step will be different.
Finally you will need to create a factories
directory under the test
directory of your project. This is where you will define your factories. This directory will be automatically loaded for you during your tests.
Almost all web applications will have a User
model and so we can define a Factory for creating new instances during testing.
Create a new file called user.rb
under the factories
directory:
FactoryGirl.define do
factory :user do
first_name Faker::Name.first_name
last_name Faker::Name.first_name
email { Faker::Internet.safe_email(first_name) }
username { Faker::Internet.user_name(first_name) }
password Faker::Internet.password
end
end
In this example I’m using the Faker gem to automatically generate the properties of this model.
Using Faker is optional as you can simply set these properties to default values.
More often than not, your models will be associated with other models in your application. Fortunately, Factory Girl makes it really easy to define these relationships.
For example, an Article
should belong to a User
and so we can define the article
Factory like this:
FactoryGirl.define do
factory :article do
title Faker::Lorem.sentence
slug { Faker::Internet.slug(title, '-') }
user
html { "<p>#{markdown}</p>" }
markdown Faker::Lorem.paragraph
end
end
As you can see, I can simply call user
and Factory Girl will automatically know to create the user association.
However, if your association has a different name, you can provide the model explicitly so Factory Girl doesn’t get confused:
association :author, factory: :user
Now that we have Factory Girl installed, configured, and we have a couple of Factories defined, we can start using it in our tests.
If you require a model object, but it doesn’t need to be saved you can use the build
method:
user = build(:user)
This will save the roundtrip to the database. As you can see you simply pass the name of the factory as a symbol to the build
method.
If you want to override certain properties of the model, you can pass a hash as an optional second parameter:
user = build(:user, first_name: 'Philip')
If you need to actually save the model to the database, you can use the create
method instead:
user = create(:user)
You can also override any of the properties of the model by passing an optional hash as the second parameter in exactly the same as we saw for the build
method:
user = build(:user, first_name: 'Philip')
Factory Girl is a great way to set up the correct scenario during your tests.
It makes it really easy to create model instances or populate the database with data to test your application under very precise circumstances.
I find it better than Fixtures for a couple of different reasons.
Firstly, I find it overwhelming when I have to maintain Fixture data and be aware of it when writing my tests.
Secondly, if I’m reading someone else’s tests, I find it much easier to understand what’s going on if the data that the test relies on is actually explicitly defined in the test.
There is actually a lot more that Factory Girl can do that we haven’t looked at today.
But we will likely see a lot more what it is capable of in the coming weeks as we see how to test specific functionality or circumstances in Ruby on Rails applications.