Back to Repositories

Testing Stub Strategy Implementation in factory_bot

This test suite validates the stub strategy implementation in FactoryBot, focusing on persistence method handling and association support. It ensures proper behavior of stubbed models while preventing database access and maintaining consistent object state.

Test Coverage Overview

The test suite comprehensively covers the FactoryBot::Strategy::Stub implementation, focusing on disabled persistence methods and object state management.

  • Validates behavior of stubbed associations
  • Tests callback handling
  • Verifies persistence state indicators
  • Ensures consistent created_at timestamp assignment

Implementation Analysis

The testing approach uses RSpec shared examples to systematically verify disabled persistence methods and object behavior patterns.

The implementation leverages RSpec’s double mocking and class definition helpers to create isolated test scenarios. Key patterns include shared behavior testing and method arity verification.

Technical Details

Testing infrastructure includes:

  • RSpec for test framework
  • Custom shared examples for reusable test cases
  • Dynamic class definition for test objects
  • Method introspection for arity checking

Best Practices Demonstrated

The test suite exemplifies high-quality testing practices through comprehensive coverage of edge cases and clear organization.

  • Shared example patterns for DRY test code
  • Explicit error message validation
  • Thorough persistence method verification
  • Isolated test scenarios

thoughtbot/factory_bot

spec/factory_bot/strategy/stub_spec.rb

            
shared_examples "disabled persistence method" do |method_name|
  let(:instance) { described_class.new.result(evaluation) }

  describe "overriding persistence method: ##{method_name}" do
    it "overrides the method with any arity" do
      method = instance.method(method_name)

      expect(method.arity).to eq(-1)
    end

    it "raises an informative error if the method is called" do
      expect { instance.send(method_name) }.to raise_error(
        RuntimeError,
        "stubbed models are not allowed to access the database - #{instance.class}##{method_name}()"
      )
    end
  end
end

describe FactoryBot::Strategy::Stub do
  it_should_behave_like "strategy with association support", :build_stubbed
  it_should_behave_like "strategy with callbacks", :after_stub
  it_should_behave_like "strategy with strategy: :build", :build_stubbed

  context "asking for a result" do
    let(:result_instance) do
      define_class("ResultInstance") {
        attr_accessor :id, :created_at
      }.new
    end

    let(:evaluation) do
      double("evaluation", object: result_instance, notify: true)
    end

    it { expect(subject.result(evaluation)).not_to be_new_record }
    it { expect(subject.result(evaluation)).to be_persisted }
    it { expect(subject.result(evaluation)).not_to be_destroyed }

    it "assigns created_at" do
      created_at1 = subject.result(evaluation).created_at
      created_at2 = subject.result(evaluation).created_at

      expect(created_at1).to equal created_at2
    end

    include_examples "disabled persistence method", :connection
    include_examples "disabled persistence method", :decrement!
    include_examples "disabled persistence method", :delete
    include_examples "disabled persistence method", :destroy
    include_examples "disabled persistence method", :destroy!
    include_examples "disabled persistence method", :increment!
    include_examples "disabled persistence method", :reload
    include_examples "disabled persistence method", :save
    include_examples "disabled persistence method", :save!
    include_examples "disabled persistence method", :toggle!
    include_examples "disabled persistence method", :touch
    include_examples "disabled persistence method", :update
    include_examples "disabled persistence method", :update!
    include_examples "disabled persistence method", :update_attribute
    include_examples "disabled persistence method", :update_attributes
    include_examples "disabled persistence method", :update_attributes!
    include_examples "disabled persistence method", :update_column
    include_examples "disabled persistence method", :update_columns
  end
end