Real World Example: Using factory_girl to simplify our test setup

When you do integration testing in a ruby on rails application, you don’t want to stub out all involved models. Rails’ built in approach of using fixtures is considered to be sub-optimal and the way to go today is to use factories.

Homegrown Factories

In our application we used to write our own factories, one for each model. But we didn’t build enough intelligence into them to be able to deal with associations automatically. For a 1-to-n relationship, we had to first create n objects of the dependent type and then the owning object, adding it’s children manually. All that happened inside the RSpec before blocks (or separate helper methods, if it became too complex). As there was no standard way of assembling your object networks we had a lot of places where we had similar setups rebuilt from scratch for every other spec. Not exactly DRY.

Factory Girl to the rescue

To improve the situation and cut down the maintenance cost of our specs, I introduced factory_girl. Factory Girl gives you a nice and concise way of defining your object networks and automatically instaniates a whole such network when requesting one of the participating objects. Let’s say you’ve got a similarly complex model as we:

UML Diagram

With factory_girl you have to define one factory per model as we did in our home grown approach. But Factory Girl takes care of associations automatically. Here are the factory definitions for the above graph:

require 'factory_girl'

Factory.define :user do |f|
  f.sequence(:user_name){ |n| "fooAPL#{n}" }
  f.password 'ups' ''

Factory.define :trademark do |f|
  f.sequence(:name) {|n| "TRADEMARK#{n}"}

Factory.define :model do |f| "The Model"
  f.association :trademark
  f.association :user

Factory.define :car do |f|
  f.association :trademark
  f.association :model "Carrr"

If you do not care about the exact attributes of the objects in the graph (you just expect them to be there with their default values), it’s now as easy as


to construct the whole object network. Even things like:


work as expected.

If you need more control over specific attributes of participating objects, just let factory_girl create them and use them in later factory_girl calls:

tm = Factory(:trademark, :name => "A very special name")
model = Factory(:model, :name => "Another special name")
car = Factory(:car, :model => model, :trademark => tm)

In that way you can customize certain attributes of your objects on the fly.

Room for improvement

Pushing Factory Girl more and more to the edge cases, we discovered that its support for polymorphic associations is not yet perfect. And its ability to return stubs instead of real ActiveRecord objects seems a little over simplified in comparison to using a fully fledged stubbing framework (which you can use easily in conjunction with factory girl). It would be great if factory girl would use one of the common mock frameworks for its factories.

Factory girl is a great tool, which enabled us to drop hundreds of lines of duplicated and overcomplicated setup code. It’s really so much nicer and cleaner.

What are your experiences with fixtures and factories? Let us know in the comments!

8 thoughts on “Real World Example: Using factory_girl to simplify our test setup

  1. We’ve been using Factory Girl with great success for something like a year now. Now we started using Machinist for new projects, which takes a lot of ideas from Factory Girl and improves on its syntax. Machinist also comes with the awesome “Sham” class, which (in conjunction with Faker) gives you random but unique test data to populate your models with required attributes you don’t care about.


  2. Hi, thanks for the article.
    Do you know if there’s a way to create a new model of the same trademark as the previous user without setting all the stuff manually? I mean, is there a way to pass a block or something like that while building the associations in the factory?
    I know I could use something like
    other_car_model = Factory(:model, :car => car.model, :trademark => car.model.trademark)
    other_user = Factory(:model => other_car_model)

    But if the business model is a little more complex (with more models and more associations) this becomes a little bit painful.

    Thanks in advance


  3. Hm, usually we use defaults specified in the factory itself. If you really need to setup a special trademark which you need to reuse over multiple cars the only thing you could do is:

    tm = Factory(:trademark)
    car = Factory(:car, :trademark => tm, :name => "car")
    other_car = Factory(:car, :trademark => tm, :name => "other")

    But I fear that is not really better than your solution.
    So, setting up sane defaults and sticking to them where ever possible is the best advice I can come up with right now, sorry.


  4. @Fernando: You can always write a method that does what you cannot describe in a factory:

    def make_car_with_last_trademark(options)
    options[:trademark] ||= Trademark.last || Factory(:trademark)
    Factory(:car, options)

    With machinist you could embed this behavior directly into a factory (blueprint in machinist-lingo) because every attribute can take a block. I’m not sure about your options with factory_girl.


  5. I’d like to be able to create two objects, A and B, and relate them both to Object C in my factories so I can dry up my code.

    I’m running into scenarios where Object A is related to one instance of Object C, and Object B is related to another instance of Object C, when I’d like them to both be relating to the same instance of Object C.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.