Arjan van der Gaag is a thirtysomething software developer,
historian and all-round geek. This is his blog about Ruby, Rails,
Javascript, Git, CSS, software and the web. See more projects or
follow Arjan at
Github,
Twitter,
LinkedIn or via his feed.

FactoryGirl Tips and Tricks

Written on
20 jun 2012
and tagged
code, ruby, testing

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:

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.

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:

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:

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:

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:

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:

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:

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.