FactoryGirl is an awesome fixture replacement library that gives you a lot of power and flexibility, at the cost of more code to maintain and increased mental overhead. It pays get to know it, so you can wield its flexibility to improve your tests and your productivity.

Get the most out of FactoryGirl

1. Use traits for modular factories

Traits are reusable pieces of attribute definitions that you can mix and match into your factories. Traits are to factories what modules are to classes a much more natural and flexible way of sharing common behaviour.

For example, say you have a Post and a Page object that both have a publication date. You could use inheritance to create various combinations:

FactoryGirl . define do factory :post do title 'New post' factory :draft_post do published_at nil end factory :published_post do published_at Date . new ( 2012 , 12 , 3 ) end end factory :page do title 'New page' factory :draft_page do published_at nil end factory :published_page do published_at Date . new ( 2012 , 12 , 3 ) end end end FactoryGirl . create :draft_page FactoryGirl . create :published_post

The repetition should be obvious. Traits can make this DRY:

FactoryGirl . define do factory :post do title 'New post' end factory :page do title 'New page' end trait :published do published_at Date . new ( 2012 , 12 , 3 ) end trait :draft do published_at nil end end FactoryGirl . create :post , :published FactoryGirl . create :page , :draft

With a simple example the difference might seem trivial, but try to think how quickly the complexity of your inheritance chain would increase if you had not one but two, six or twelve different attributes (or sets of attributes) you wanted to be able to apply in different combinations.

Traits are awesome because they can define callbacks, ignored attributes and even nest other traits.

2. Use ignored attributes to tweak callbacks

FactoryGirl lets you define ignored attributes, which will not be set on your newly created object. This is surprisingly useful in combination with dependent attributes and callbacks, which do get to access them.

Consider an example of a blog post with comments:

FactoryGirl . define do factory :comment do author 'Anonymous' body 'Great post, man!' approved_at Date . new ( 2012 , 3 , 6 ) post end factory :post do title 'New post' end trait :with_comments do after :create do | post | FactoryGirl . create_list :comment , 3 , :post => post end end end

It is trivial to create a post with three comments by applying the with_comments trait to a factory invocation. But what if we wanted to adjust the number of comments? We can use an ignored attribute:

trait :with_comments do ignore do number_of_comments 3 end after :create do | post , evaluator | FactoryGirl . create_list :comment , evaluator . number_of_comments , :post => post end end

Note that a special second argument is passed to the callback block, the evaluator, which knows about the ignored attributes. Now you can simply pass in the ignored attribute like you do a regular attribute:

FactoryGirl . create :post , :with_comments , :number_of_comments => 4

Make trivial variations possible with ignored attributes removes the need for tons of almost-identical factories.

3. Create non-generic examples

Your factories are used in tests with as goal to make assertions about their state and behaviour. It can really help to use very specific example objects with well-known attribute values. So don’t create a person_with_three_comments_and_a_post ; instead use a person_mike and person_john . Use your factories to let your tests tell a simple story about your objects.

4. Use aliases

FactoryGirl allows you to define aliases to existing factories to make them easier to re-use. This could come in handy when, for example, your Post object has a author attribute that actually refers to an instance of a User class. While normally FactoryGirl can infer the factory name from the association name, in this case it will look for a author factory in vain. So, alias your user factory:

FactoryGirl . define do factory :user , :aliases => [ :author ] do username 'anonymous' end factory :post do author # => populated with the user factory end end

5. allow setting up common associations

When you have many business models with many associations to other business models, you quickly end up with tests that first have to employ a dozen objects before the object under test is in such a state that meaningful queries can be made about it. This might be a sign of bad design, but could very well be unavoidable. In case of the latter, consider creating traits and factories that preload such associations for you, as with the example of a post with comments:

FactoryGirl . define do factory :post do title 'New post' end trait :with_comments do after :create do | post | FactoryGirl . create_list :comment , 3 , :post => post end end end FactoryGirl . create :post , :with_comments

Common pitfalls when creating factories

1. Do not use random attribute values

One common pattern is to use a fake data library (like Faker or Forgery) to generate random values on the fly. This may seem attractive for names, email addresses or telephone numbers, but it serves no real purpose. Creating unique values is simple enough with sequences:

FactoryGirl . define do sequence ( :title ) { | n | "Example title #{ n } " } factory :post do title end end FactoryGirl . create ( :post ). title # => 'Example title 1'

Your randomised data might at some stage trigger unexpected results in your tests, making your factories frustrating to work with. Any value that might affect your test outcome in some way would have to be overridden, meaning:

Over time, you will discover new attributes that cause your test to fail sometimes. This is a frustrating process, since tests might fail only once in every ten or hundred runs – depending on how many attributes and possible values there are, and which combination triggers the bug. You will have to list every such random attribute in every test to override it, which is silly. So, you create non-random factories, thereby negating any benefit of the original randomness.

One might argue, as Henrik Nyh does, that random values help you discover bugs. While possible, that obviously means you have a bigger problem: holes in your test suite. In the worst case scenario the bug still goes undetected; in the best case scenario you get a cryptic error message that disappears the next time you run the test, making it hard to debug. True, a cryptic error is better than no error, but randomised factories remain a poor substitute for proper unit tests, code review and TDD to prevent these problems.

Randomised factories are therefore not only not worth the effort, they even give you false confidence in your tests, which is worse than having no tests at all.

2. Test for explicit values

In addition to random values being bad, relying on your factories default values may be bad idea, too. Unless you create specific story-telling factories, such as “john” rather than “user1”, you should anticipate someone else (i.e. you, four weeks from now) changing the default factory values. When you are testing, you want to test for explicit values your test controls. A test like this is silly:

FactoryGirl . define do factory :post do title { Forgery ( :lorem_ipsum ). words ( 5 ) } end end describe 'Blog' do it 'should show the post title on the page' do post = FactoryGirl . create :post visit '/blog' page . should have_content ( post . title ) end end

What happens when your post title randomly turns out to be an empty string, or a phrase that also happens to occur somewhere else on the page? If your test value is random or outside your control, how can you prove something about it? Consider this improved example and its increased readability:

it 'should show the post title on the page' do post = FactoryGirl . create :post , :title => 'My example post' visit '/blog' page . should have_content ( 'My example post' ) end

The explicit title and test value underline the intent of the test and leave far less room for false positives or future changes.

3. Do not use dynamic values by default

Dynamic attribute values are evaluated on invocation time rather than evaluation time. This allows you to use, for example, the current time in your factories:

FactoryGirl . define do factory :post do created_at Time . now # => will be the same for every object published_at { Time . now } # => will be updated for every object end end

This does not mean, however, that you should alway use dynamic attribute values when a simple static value will do fine:

FactoryGirl . define do factory :post do title { 'My new post' } # silly! body 'Lorem ipsum' # pretty! end end

Although I’m sure there will be minor performance benefits to using static values, that is not the point. We want our tests to be readable and clear. Using blocks everywhere conceals the true meaning of the actually dynamic attributes between the static attributes.

4. Do not manually create associations

When using Rails, FactoryGirl is smart enough to know how to set up associations for you. So don’t do this:

FactoryGirl . define do factory :comment do post { FactoryGirl . create :post } end end

Usually you can just use another factory name as an attribute, or use the association method to customise it:

FactoryGirl . define do factory :comment do post end factory :user do username 'anonymous' end factory :post do association :author , :factory => :user , :username => 'admin' end end

Note how the comment factory simply uses the post factory to set its post attribute. The post factory however uses the user factory to populate its author attribute and provides some additional details on how it is to be built.

Further reading

If you have any pet peeves when using FactoryGirl, let me know on Twitter!