factory_bot

repository·main·Indexed 27 days ago

https://github.com/thoughtbot/factory_bot

A Ruby library designed to replace traditional fixtures with a flexible, syntax-driven approach to generating test data. It supports multiple build strategies, factory inheritance, and complex object graphs. The library includes features for defining factory aliases, managing associations with custom build strategies, and generating lists of records via build_list, create_list, and build_stubbed_list. It also provides ActiveSupport::Notifications for tracking factory execution, compilation, and debugging.

Tokens
35.3K
Snippets
140
Records
196
Agent score
91%

What's inside factory_bot

  1. Overview of factory_bot

    main

    factory_bot is a fixtures replacement library for Ruby. It provides a straightforward definition syntax and supports several key features:

    • Multiple build strategies: Create saved instances, unsaved instances, attribute hashes, and stubbed objects.
    • Multiple factories per class: Define different factories for the same class (e.g., user, admin_user).
    • Factory inheritance: Build new factories based on existing ones.
  2. Introduction to factory_bot

    main

    factory_bot is a fixtures replacement for Ruby that provides a straightforward definition syntax. It supports:

    • Multiple build strategies: including saved instances, unsaved instances, attribute hashes, and stubbed objects.
    • Multiple factories for the same class: allowing you to define different variations (e.g., user, admin_user) for a single model.
    • Factory inheritance: enabling specialized factories to inherit attributes from base factories.
  3. Use factory inheritance for better test data management

    main
    To maintain clean and scalable test suites, follow the pattern of defining a basic 'parent' factory for each class that contains only the minimum attributes required for valid instantiation. Then, create more specific 'child' factories that inherit from this parent to represent different states or variations of the object.
  4. Define factories using FactoryBot.define

    main
    To define factories, sequences, and traits, wrap your definitions in a FactoryBot.define block. This block is evaluated within the FactoryBot::Syntax::Default::DSL context, which provides access to the DSL methods required to build your test data structures.
  5. Register and use a custom strategy

    main

    After defining a custom strategy class, register it with FactoryBot using register_strategy. Once registered, you can invoke the strategy by calling a method named after the strategy symbol on FactoryBot.

    # 1. Define the strategy
    class JsonStrategy
      def initialize
        @strategy = FactoryBot.strategy_by_name(:create).new
      end
    
      delegate :association, to: :@strategy
    
      def result(evaluation)
        @strategy.result(evaluation).to_json
      end
    
      def to_sym
        :json
      end
    end
    
    # 2. Register the strategy
    FactoryBot.register_strategy(:json, JsonStrategy)
    
    # 3. Use the strategy
    FactoryBot.json(:user)
  6. Override associations using attribute overrides

    main

    When building or creating objects, you can link associated objects by passing an existing instance as an attribute override. This allows you to specify exactly which associated object should be used instead of letting factory_bot create a new one via the default association definition.

    FactoryBot.define do
      factory :author do
        name { 'Taylor' }
      end
    
      factory :post do
        author
      end
    end
    
    eunji = build(:author, name: 'Eunji')
    post = build(:post, author: eunji)
  7. Reference sequences using URIs

    main

    To manipulate specific sequences—such as generating values with generate, generating lists with generate_list, setting values with FactoryBot.set_sequence, or rewinding with rewind_sequence—you must reference them using a unique URI.

    A URI is composed of up to three components:

    1. Factory name: Required if the sequence is defined within a Factory or a Factory Trait.
    2. Trait name: Required if the sequence is defined within a Trait.
    3. Sequence name: Always required.

    You can provide the URI as individual symbols, individual strings, or a single slash-separated resource string.

  8. Access transient attributes within other attributes

    main

    You can reference transient attributes when defining the values of other attributes in a factory. This allows you to use transient state to conditionally modify or construct standard attributes.

    factory :user do
      transient do
        rockstar { true }
      end
    
      name { "John Doe#{" - Rockstar" if rockstar}" }
    end
    
    create(:user).name
    #=> "John Doe - ROCKSTAR"
    
    create(:user, rockstar: false).name
    #=> "John Doe"
  9. Create a Rake task for linting factories

    main

    To automate factory linting, you can create a Rake task. The following implementation uses an ActiveRecord transaction to ensure that the records created during linting are rolled back, leaving your test database clean. It also includes logic to handle different environments.

    # lib/tasks/factory_bot.rake
    namespace :factory_bot do
      desc "Verify that all FactoryBot factories are valid"
      task lint: :environment do
        if Rails.env.test?
          conn = ActiveRecord::Base.connection
          conn.transaction do
            FactoryBot.lint
            raise ActiveRecord::Rollback
          end
        else
          system("bundle exec rake factory_bot:lint RAILS_ENV='test'")
          fail if $?.exitstatus.nonzero?
        end
      end
    end
  10. Configure factory definitions in Rails

    main

    In Rails applications using factory_bot_rails, FactoryBot.find_definitions is called automatically after initialization. You can customize the file paths used for loading definitions in two ways:

    1. Set .definition_file_paths within a Rails initializer (e.g., config/initializers/factory_bot.rb).
    2. Use the Rails configuration object: Rails.application.config.factory_bot.definition_file_paths.