Back to Repositories

Testing Factory Creation Patterns in factory_bot

This test suite examines the create functionality in Factory Bot, focusing on instance creation and association handling. It validates various creation scenarios including custom creation blocks, evaluator usage, and block-based creation patterns.

Test Coverage Overview

The test suite provides comprehensive coverage of Factory Bot’s create functionality:

  • Basic instance creation with associations
  • Strategy-specific association creation
  • Custom creation blocks with persistence
  • Evaluator integration in creation process
  • Block-based creation patterns

Implementation Analysis

The testing approach uses RSpec’s describe/it blocks to organize distinct creation scenarios. It leverages Factory Bot’s syntax methods and model definitions to test factory creation behavior, with particular attention to association handling and custom creation blocks.

The implementation demonstrates Factory Bot’s flexibility in handling different creation strategies and persistence patterns.

Technical Details

Key technical components include:

  • RSpec testing framework
  • Factory Bot’s Syntax::Methods module
  • Dynamic model definition using define_model
  • Custom class definition with define_class
  • Transient attributes for evaluator testing

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Isolated test scenarios with clear setup
  • Explicit expectation setting
  • Comprehensive edge case coverage
  • Clean separation of concerns
  • Effective use of before blocks for setup

thoughtbot/factory_bot

spec/acceptance/create_spec.rb

            
describe "a created instance" do
  include FactoryBot::Syntax::Methods

  before do
    define_model("User")

    define_model("Post", user_id: :integer) do
      belongs_to :user
    end

    FactoryBot.define do
      factory :user

      factory :post do
        user
      end
    end
  end

  subject { create("post") }

  it { should_not be_new_record }

  it "assigns and saves associations" do
    expect(subject.user).to be_kind_of(User)
    expect(subject.user).not_to be_new_record
  end
end

describe "a created instance, specifying strategy: :build" do
  include FactoryBot::Syntax::Methods

  before do
    define_model("User")

    define_model("Post", user_id: :integer) do
      belongs_to :user
    end

    FactoryBot.define do
      factory :user

      factory :post do
        association(:user, strategy: :build)
      end
    end
  end

  subject { create(:post) }

  it "saves associations (strategy: :build only affects build, not create)" do
    expect(subject.user).to be_kind_of(User)
    expect(subject.user).not_to be_new_record
  end
end

describe "a custom create" do
  include FactoryBot::Syntax::Methods

  before do
    define_class("User") do
      def initialize
        @persisted = false
      end

      def persist
        @persisted = true
      end

      def persisted?
        @persisted
      end
    end

    FactoryBot.define do
      factory :user do
        to_create(&:persist)
      end
    end
  end

  it "uses the custom create block instead of save" do
    expect(FactoryBot.create(:user)).to be_persisted
  end
end

describe "a custom create passing in an evaluator" do
  before do
    define_class("User") do
      attr_accessor :name
    end

    FactoryBot.define do
      factory :user do
        transient { creation_name { "evaluator" } }

        to_create do |user, evaluator|
          user.name = evaluator.creation_name
        end
      end
    end
  end

  it "passes the evaluator to the custom create block" do
    expect(FactoryBot.create(:user).name).to eq "evaluator"
  end
end

describe "calling `create` with a block" do
  include FactoryBot::Syntax::Methods

  before do
    define_model("Company", name: :string)

    FactoryBot.define do
      factory :company
    end
  end

  it "passes the created instance" do
    create(:company, name: "thoughtbot") do |company|
      expect(company.name).to eq("thoughtbot")
    end
  end

  it "returns the created instance" do
    expected = nil
    result = create(:company) { |company|
      expected = company
      "hello!"
    }
    expect(result).to eq expected
  end
end