Taming Factory Bot List Creation

Derek Prior

There are times in your tests when you need to create multiple instances of the same type of object. FactoryBot1 provides create_list for just these sorts of scenarios. It’s typically sufficient to create two records in these cases, but create_list leaves open the possibility that you could unintentionally create many more records.

Consider, for example, testing a feature that pages in additional records on demand. You would likely want to use something like Kaminari to expose the paging functionality. We did just this on a recent project.

scenario 'user can page in additional records' do
  posts = create_list(:post, Post.default_per_page + 1)

  click_button "Load More"

  expect(page).to have_content(posts.first.body)
  expect(page).to have_content(posts.last.body)

The trouble is that Kaminari defaults the default_per_page value to 25. This test will create 26 records in your database. Seems a bit much, no? The simplest way to solve this is to stub the Post.default_per_page method in the test, but you will have to remember to do this in every place you test paging. Not so bad if you’re only paging Post, but what about Comment, Author, etc?

Sane Kaminari Defaults in Test

The first step we took towards solving this was to introduce an initializer that defaults the Kaminari page size to 1 record in the test environment.

Kaminari.configure do |config|
  if Rails.env.test?
    config.default_per_page = 1

This works great until someone comes along and overrides the default page size on Post with paginates_per(20). Now you’re back to creating 21 records in your test or having to remember to stub the default_per_page method on models that override the Kaminari default.

A Safety Net: Let’s Patch Factory Bot

The application we’re working on has a lot of paging. I half-jokingly suggested we override create_list to prevent gigantic list creation. When the idea wasn’t met with immediate guffaws, I went to work. I added the following to our FactoryBot configuration that is required from spec_helper:

require 'factory_bot_rails'

module FactoryBot
  module Syntax
    module Methods
      alias_method :original_create_list, :create_list

      def create_list(name, amount, *traits_and_overrides, &block)
        if amount > 2
          raise ArgumentError, "You asked to create #{amount} records. Don't do that."

        original_create_list(name, amount, *traits_and_overrides, &block)

RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods

Calls to create_list will now raise if you attempt to create more than two records. In the case of our paging example, this signals that we forgot to stub the defaults_per_page method. If you have a legitimate need to create more than two records, you can call original_create_list.

Interestingly, this change surfaced a number of non-paging tests that were creating three records where even just one would do.

Project name history can be found here.

  1. Looking for FactoryGirl? The library was renamed in 2017.