Den Dribbles

Up and running with Factory Bot in Rails 5

July 02, 2020

This post is a short getting-up-and-running style of post.

It expects that you have Rails 5 setup and ready to roll.

Why Factory Bot?

From the world’s most reliable resource Wikipedia:

Factory Bot is often used in testing Ruby on Rails applications; where it replaces Rails’ built-in fixture mechanism. Rails’ default setup uses a pre-populated database as test fixtures, which are global for the complete test suite. Factory Bot, on the other hand, allows developers to define a different setup for each test and thus helps to avoid dependencies within the test suite.

There is more info on the why on the Why Factories article.

This is simply a quick start to get up and going to test model validation.

Quick start

rails new <project> -- api
cd <project>
gem install rspec factory_bot_rails

Update Gemfile config

In the Gemfile:

group :development, :test do
  gem 'factory_bot_rails', '~>6.0'
  gem 'rspec-rails', '>= 3.9.0'
end

Run bundle install.

Automatic factory definition loading

From the docs:

By default, factory_bot_rails will automatically load factories defined in the following locations, relative to the root of the Rails project:

factories.rb
test/factories.rb
spec/factories.rb
factories/*.rb
test/factories/*.rb
spec/factories/*.rb

If you want to, you can set custom configuration in config/application.rb or the appropraite env config.

config.factory_bot.definition_file_paths = ["custom/factories"]

This will cause factory_bot_rails to automatically load factories in custom/factories.rb and custom/factories/*.rb.

Config

Add the following configuration to test/support/factory_bot.rb:

# test/support/factory_bot.rb
require "factory_bot_rails"

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

Be sure to require that file in test/test_helper.rb:

# test/test_helper.rb
ENV["RAILS_ENV"] ||= "test"
require_relative "../config/environment"
require_relative "./support/factory_bot"
require "rails/test_help"
require "rspec/rails"

class ActiveSupport::TestCase
  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
  fixtures :all

  # Add more helper methods to be used by all tests here...
end

Create a model

From the guides, we are going to generate a new model.

rails generate model Article title:string text:text
# run the migration
rails db:migrate

If successful, the migration should return:

==  CreateArticles: migrating ==================================================
-- create_table(:articles)
   -> 0.0019s
==  CreateArticles: migrated (0.0020s) =========================================

Update Ruby

Update app/models/article.rb to look like the following:

class Article < ApplicationRecord
  validates :title, presence: true, length: {minimum: 5}
  validates :text, presence: true, length: {minimum: 5}
end

Add the following to the factories directory

# test/factories/articles.rb
FactoryBot.define do
  factory :article do
    title { "MyString" }
    text { "MyText" }
  end
end

Add an Rspec for the model

# test/models/article_test.rb
require "./test/test_helper"

class ArticleTest < ActiveSupport::TestCase
  describe "article model" do
    before(:all) do
      @article1 = FactoryBot.create(:article)
    end

    it "is valid with valid attributes" do
      expect(@article1).to be_valid
    end

    it "is not valid without a title" do
      article2 = FactoryBot.build(:article, title: nil)
      expect(article2).to_not be_valid
    end

    it "is not valid without text" do
      article2 = FactoryBot.build(:article, text: nil)
      expect(article2).to_not be_valid
    end

    it "is not valid without a title of min length 5" do
      article2 = FactoryBot.build(:article, title: "Min")
      expect(article2).to_not be_valid
    end

    it "is not valid without text of min length 5" do
      article2 = FactoryBot.build(:article, text: "Min")
      expect(article2).to_not be_valid
    end
  end
end

Running the test

rspec test/models/article_test.rb

We should get something like the following out:

.....

Finished in 0.04765 seconds (files took 0.90722 seconds to load)
5 examples, 0 failures

Run options: --seed 18801

# Running:



Finished in 0.001607s, 0.0000 runs/s, 0.0000 assertions/s.
0 runs, 0 assertions, 0 failures, 0 errors, 0 skips

Resources and Further Reading

  1. thoughtbot/factory_bot_rails
  2. thoughtbot/factory_bot
  3. Why Factories?
  4. Creating an Article model in Rails
  5. Testing RSpec

Image credit: Alex Knight


A personal blog on all things of interest. Written by Dennis O'Keeffe, Follow me on Twitter