cult3

Replacing Fixtures with Factory Girl in Ruby on Rails

Feb 17, 2016

Table of contents:

  1. What is the problem with Fixtures?
  2. How is Factory Girl different?
  3. Installing Factory Girl
  4. Defining your first Factory
  5. Defining Associations
  6. Using Factory Girl in your tests
  7. Conclusion

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.

What is the problem with Fixtures?

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.

How is Factory Girl different?

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.

Installing Factory Girl

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.

Defining your first Factory

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.

Defining Associations

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

Using Factory Girl in your tests

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')

Conclusion

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.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.