---
title: Speed Up Tests by Selectively Avoiding Factory Bot
teaser: 'Learn one simple way to speed up your tests with the factory_bot gem!

  '
tags: web,rails,testing,rspec,factory_girl,factory_bot
author: Josh Clayton
published_on: 2014-08-14
---

I've talked about speeding up unit tests when using
[Factory Bot][factory-bot-github] by relying on
[`FactoryBot.build_stubbed`][build-stubbed-post], but there's another
surefire way to speed up your test suite with Factory Bot.

Don't use it.

## Most Unit Tests Don't Need Persisted Data

There are plenty of times where data needs to exist in the database to
accurately test an application; most acceptance tests will require some amount
of data persisted (either via Factory Bot or by creating data driven through
UI interactions). When unit-testing most methods, however, Factory Bot (and
even persisting data to the database) is unnecessary.

Let's start with a couple of tests around a method we'll need to define,
`User#age`:

```ruby
describe User do
  describe "#age" do
    it "calculates age given birthdate" do
      user = generate_user_born_on 366.days.ago

      expect(user.age).to eq 1
    end

    it "calculates age correctly by rounding age down to the appropriate integer" do
      user = generate_user_born_on 360.days.ago

      expect(user.age).to eq 0
    end

    def generate_user_born_on(date)
      FactoryBot.create :user, birthdate: date
    end
  end
end
```

This seems like a harmless use of Factory Bot, and leads us to define
`User#age`:

```ruby
class User < ActiveRecord::Base
  def age
    ((Date.current - birthdate)/365.0).floor
  end
end
```

Running the specs:

    rspec spec/models/user_spec.rb
    ..

    Finished in 0.01199 seconds
    2 examples, 0 failures

## More than 100% Faster

Looking at `User#age`, though, we don't actually care about the database.
Let's swap `FactoryBot.create` with `User.new` and re-run the spec.

    rspec spec/models/user_spec.rb
    ..

    Finished in 0.00489 seconds

Still a green suite, but more than 100% faster.

## Associations Make a Test Suite Slower

Now, let's imagine `User` grows and ends up having a `Profile`:

```ruby
class User < ActiveRecord::Base
  has_one :profile

  def age
    ((Date.current - birthdate)/365.0).floor
  end
end
```

We update the factory, including the associated profile:

```ruby
FactoryBot.define do
  factory :user do
    profile
  end

  factory :profile
end
```

Let's re-run the spec using Factory Bot:

    rspec spec/models/user_spec.rb
    ..

    Finished in 0.02278 seconds
    2 examples, 0 failures

Whoa, it's now taking twice as long as it was before, but absolutely zero
tests changed, only the factories.

Let's run it again, but this time using `User.new`:

    rspec spec/models/user_spec.rb
    ..

    Finished in 0.00474 seconds
    2 examples, 0 failures

Whew, back to a reasonable amount of time, and we're still green. What's going
on here?

## Persisting Data is Slow

`FactoryBot.create` creates two records in the database, a user and a
profile. Persistence is slow, which we know, but because Factory Bot is
arguably easy to write and use, it hides it well. Even changing from
`FactoryBot.create` to `FactoryBot.build` doesn't help much:

    rspec spec/models/user_spec.rb
    ..

    Finished in 0.01963 seconds
    2 examples, 0 failures

That's because `FactoryBot.build` creates associations; so, every time we use
Factory Bot to build a `User`, we're still persisting a `Profile`.

## Writing to Disk Makes Things Worse

Sometimes, objects will write to disk during the object's persistence
lifecycle. A common example is processing a file attachment during an
ActiveRecord callback through gems like [Paperclip][paperclip] or
[Carrierwave][carrierwave], which may result in processing thousands of files
unnecessarily. Imagine how much more slowly a test suite is because data is
being created.

It's incredibly difficult to identify these bottlenecks because of the
differences between `FactoryBot.build`, `FactoryBot.create`, and how
associations are handled. By remembering to use `FactoryBot.build` on an
avatar factory, we may speed up some subset of tests, but if `User` has an
avatar associated with it, even when calling `FactoryBot.build(:user)`,
avatars still get created - meaning valuable time spent processing images and
persisting likely unnecessary data.

## How to Fix Things

`User#age` is a great example because it's quite clear that there's no
interaction with the database. Many methods on core domain objects will
have methods like these, and I suggest avoiding Factory Bot entirely in
these, if possible. Instead, instantiate the objects directly, with the
correct data necessary to test the method. In the example above, `User#age`
relies only on one point of data: `birthdate`. Since that's the method being
tested, there's no need to instantiate a `User` with anything else. It
provides clarity to yourself and other developers by explicitly defining the
set of data it's using for the test.

When testing an object and collaborators, consider [doubles] like fakes or
stubs.

My general advice, though, is to avoid Factory Bot as much as is reasonably
possible. Not because it's bad or unreliable software (Factory Bot is very
reliable; we've used it successfully since 2008), but because its inherent
persistence mechanism is calling `#save!` on the object, which will always
take longer than *not* persisting data.

* * *

**Disclaimer:**

Looking for FactoryGirl? The library was renamed in 2017.
[Project name history can be found here.][factory-bot-github-name-change]

[factory-bot-github]: https://github.com/thoughtbot/factory_bot
[build-stubbed-post]: https://thoughtbot.com/blog/use-factory-bots-build-stubbed-for-a-faster-test
[paperclip]: https://github.com/thoughtbot/paperclip
[carrierwave]: https://github.com/carrierwaveuploader/carrierwave
[doubles]: http://www.martinfowler.com/bliki/TestDouble.html
[factory-bot-github-name-change]: https://github.com/thoughtbot/factory_bot/blob/master/NAME.md
