如何在工厂内将参数传递给 FactoryGirl 的 `build()` 或 `create()` class

How to get the arguments passed into FactoryGirl's `build()` or `create()` within the factory class

鉴于:

FactoryGirl.define do
  factory :user do
    name ''
    email ''

    after(:build) do |user, evaluator|
      # is there any way to get the following returned result?
      puts some_code_that_will_return_the_arguments_passed    
      # => {name: 'Foo', email: 'foobar@example.com'}
    end
  end
end

FactoryGirl.build(:user, name: 'Foo', email: 'foobar@example.com')

我的解决方法:

FactoryGirl.define do
  factory :user do
    transient do
      build_from []
    end

    name ''
    email ''

    after(:build) do |user, evaluator|
      puts evaluator.build_from.map{|attribute| [attribute, user[attribute]] }.to_h
      # => {name: 'Foo', email: 'foobar@example.com'}
    end
  end
end

FactoryGirl.build(:user, name: 'Foo', email: 'foobar@example.com', build_from: [:name, :email])

问题背景:

不幸的是,我无法从官方文档或互联网上找到任何内容。

在检查 factory_girl 源代码并阅读文档后,我找不到任何 public API 来访问参数, 因此我写了自己的 "safe" monkey-patch.

重要提示:

  • 此 monkey-patch 仅经过测试与 factory_girl 版本 4.7.0 兼容。如果你的不一样,首先确保 factory_girl-X.X.X/lib/factory_girl/evaluator.rb 中的 def initialize 完全相同(仅猴子补丁修改除外)

解决方案:

  • 创建一个新文件:config/initializers/factory_girl_patch.rb 包含以下内容:

    require 'factory_girl/evaluator'
    
    module FactoryGirl
      class Evaluator
        attr_reader :overrides_arguments # MONKEY-PATCH: ADDED LINE
    
        def initialize(build_strategy, overrides = {})
          # MONKEY-PATCH-START: ADDED LINES
          monkey_patch_tested_version = '4.7.0'
          if Gem.loaded_specs['factory_girl'].version.version != monkey_patch_tested_version
            raise "This monkey-patched code is only tested on #{monkey_patch_tested_version}. Verify source code differences with your version first by inspecting lib/factory_girl/evaluator.rb file."
          end
          @overrides_arguments = overrides.clone # WHAT WE NEED
          # MONKEY-PATCH-END
    
          @build_strategy = build_strategy
          @overrides = overrides
          @cached_attributes = overrides
          @instance = nil
    
          @overrides.each do |name, value|
            singleton_class.define_attribute(name) { value }
          end
        end
      end
    end
    

用法:

FactoryGirl.define do
  factory :user do
    name ''
    email ''

    after(:build) do |user, evaluator|
      puts evaluator.overrides_arguments
      # => {name: 'Foo', email: 'foobar@example.com'}
    end
  end
end

FactoryGirl.build(:user, name: 'Foo', email: 'foobar@example.com')