---
title: Why Factories?
teaser: Understanding _why_ we use factories helps us write faster, more readable
  tests.
tags: ruby,web,testing
author: Joël Quenneville
published_on: 2018-01-12
---

These days, most Rails projects use some form of factories in their test set up.
What problem do they solve and why are they needed?

## Too much info

Given a `User` model with `first_name`, `last_name`, and `location` fields, we
could write a test like:

```ruby
describe "#full_name" do
  it "combines first and last name" do
    user = User.new(
      first_name: "Joël",
      last_name: "Quenneville",
      location: "Boston"
    )

    expect(user.full_name).to eq "Joël Quenneville"
  end
end
```

This test has _too much_ information in our test setup. The location is
irrelevant to the output of the `full_name` method yet it's hard to know that
just reading the test. It looks like location has some impact on calculating the
full name but we can't know for sure without looking at the source.

## Too little info

```ruby
it "combines first and last name" do
  expect(the_test_user.full_name).to eq "Joël Quenneville"
end
```

This test has _too little_ information in the setup. Why is the full name `"Joël
Quenneville"`? Where does that come from? Is that one value or multiple combined
together? There is no way of knowing here.

## Just Right

```ruby
it "combines first and last name" do
  user = User.new(first_name: "Joël", last_name: "Quenneville")

  expect(user.full_name).to eq "Joël Quenneville"
end
```

This has just the right amount of information. All of the data that influences
the result is there in the setup. None of the irrelevant data is included.

Two rules of thumb when writing tests are:

1. Include _all_ data that impacts the expectation in the setup phase
2. Include _none_ of the data that does not impact the expectation

## Required fields

It seems pretty easy to follow these two rules of thumb until you run into
required fields. If `User` were an ActiveRecord model that looked like:

```ruby
class User < ApplicationRecord
  validates :first_name, presence: true
  validates :last_name, presence: true
  validates :location, presence: true
end
```

Then this test

```ruby
it "downcases the location on save" do
  user = User.new(location: "Boston")

  user.save

  expect(user.location).to eq "boston"
end
```

will fail with a validation error. Our validations _require_ us to have
irrelevant information.

We can try to avoid that with a helper function:

```ruby
it "downcases the location on save" do
  user = build_user_with_defaults(location: "Boston")

  user.save

  expect(user.location).to eq "boston"
end

def build_user_with_defaults(overrides)
  defaults = {
    first_name: "Default",
    last_name: "Default",
    location: "Default"
  }

  User.new(defaults.merge(overrides))
end
```

Once more, our test contains all the relevant information in its setup but no
more.

## Factories

This idea of having a method to build an object while pre-filling defaults is
inspired by the [factory method pattern]. Because of this, such methods are
often referred to as "factories" in the testing world.

This approach to setting up test data is so helpful that many languages have
libraries for writing test factories such as Ruby's [FactoryBot], Elixir's [Ex
Machina], and Python's [factory_boy] among others.

Using FactoryBot we might write:

```ruby
factory :user do
  first_name { "default" }
  last_name { "default" }
  location { "default" }
end
```

and then write a test like:

```ruby
it "downcases the location on save" do
  user = create(:user, location: "Boston")

  expect(user.location).to eq "boston"
end
```

[factory method pattern]: https://practicingruby.com/articles/creational-design-patterns#factory-method
[FactoryBot]: https://github.com/thoughtbot/factory_bot
[Ex Machina]: https://github.com/thoughtbot/ex_machina
[factory_boy]: https://github.com/FactoryBoy/factory_boy

## Abusing factories

Just as with the regular constructor, it's possible to abuse factories by
cluttering up the test with irrelevant information.

```ruby
it "downcases the location on save" do
  user = create(:user, location: "Boston", first_name: "Joël")

  expect(user.location).to eq "boston"
end
```

It's also possible to [rely on the default values], leading to [mystery guests]
and possibly even tautological tests.

```ruby
it "downcases the location on save" do
  user = create(:user)

  expect(user.location).to eq "default"
end
```

In many cases, you [don't even need to use a factory]. If your object doesn't
have required attributes or you don't need to satisfy Rail's validations for a
particular test, then you can get away with just setting the relevant properties
directly.

Test factories exist to solve a specific problem. If your tests don't suffer
from that problem then don't just blindly use a factory. Conversly, if you are
using a factory, be wary of recreating the exact same problem that led to
factories in the first place by adding too much data.

In all cases, keeping in mind _why_ you use factories and what problems they
solve will help you write tests that are easier to read and faster to run.

[don't even need to use a factory]: https://thoughtbot.com/blog/speed-up-tests-by-selectively-avoiding-factory-bot
[rely on the default values]: https://thoughtbot.com/blog/factories-should-be-the-bare-minimum
[mystery guests]: https://thoughtbot.com/blog/mystery-guest
