Getting Sequential: A look into a Factory Bot Anti-pattern

Josh Clayton

I’ve noticed a handful of what I’d consider Factory Bot anti-patterns, either through pull requests, GitHub issues, or questions spread across Stack Overflow, Google Groups, etc. The more common they are, the more I realize that it will benefit plenty of other developers down the road if a list of anti-patterns can be identified and what to do instead.

The one I’d like to outline today is based off of a request to make Factory Bot sequences reset after every test run. The idea of resetting sequences after every run is that then you can start to program against these names in step definitions. For example, imagine the factory:

FactoryBot.define do
  factory :category do
    sequence(:name) {|n| "Category #{n}" }
  end

  # ... more factories
end

And the scenario:

Scenario: Create a post under a category
  Given a category exists
  And I am signed in as an admin
  When I go to create a new post
  And I select "Category 1" from "Categories"
  And I press "Create"
  And I go to view all posts
  Then I should see a post with the category "Category 1"

That first step is given to us by Factory Bot (check out the Cucumber integration on the factory_bot wiki to find out more) and will create a category without overriding any of the defaults provided by that factory.

If all sequences were reset after every run, it’d be safe to assume that the category created as the first step of the scenario will create a category named “Category 1”.

There are two problems with this approach. Firstly, we’re now tightly coupling the scenarios to naming conventions outlined in the factories. If the sequence logic changes, all the scenarios that rely on that naming convention will break. Not fun. Secondly, it’s not at all obvious where that “Category 1” record was created. If I’m a developer who’s looking at this code with a fresh set of eyes, I’d assume that this category was created from a global setup or seed file instead of being assigned in the first step of the scenario.

The solution is to name the category explicitly.

Scenario: Create a post under a category
  Given a category exists with a name of "Category 1"
  And I am signed in as an admin
  When I go to create a new post
  And I select "Category 1" from "Categories"
  And I press "Create"
  And I go to view all posts
  Then I should see a post with the category "Category 1"

The first step is, again, given by Factory Bot and allows us to set the name attribute. It’s now immediately obvious that the category we’re creating is the one we’re using throughout the scenario, we’re not reliant on a naming convention outlined by Factory Bot, and any other developer looking at the code won’t question where this category was created.

Factory Bot’s sequences are a great way to ensure that models with uniqueness don’t run into any naming collisions, and it’s handy to have recognizable data (instead of having every bit of data in tests runs be virtually the same). That said, when writing tests against data where the values matter, don’t leave those values to chance; assign them outright. Give them weight. The intent is much more obvious, less error prone, and requires no patches to Factory Bot.


Disclaimer:

Looking for FactoryGirl? The library was renamed in 2017. Project name history can be found here.