Rspec / 在我的 rails 应用程序中创建新实例时,FactoryBot 工厂不是 运行 after_initialize

Rspec / FactoryBot factories are not running after_initialize when createing a new instance in my rails app

我有一些模型像这样使用 after_initialize 挂钩来设置大量默认属性和其他东西:

after_initialize do |some_model|
  initializer(some_model)
end

def initializer(some_model)
  #... loads of pre-processing
end

这一切 运行 在开发和生产中都很棒,但是在我的 FactoryBot 和 rspec 测试中,这个挂钩不是 运行ning。我有一个看起来像这样的工厂:

FactoryBot.define do
  factory :some_model do
    some_number { 1 }
    is_it_something { false }
    account { create(:user).accounts.first }
  end
end

但我的测试看起来像这样:

some_model = create(:some_model)
some_model.initializer(some_model)

问题是我必须在创建 FactoryBot 模型后单独调用 initializer。这导致了各种问题,因为 create() 触发了创建验证,但它们失败了,因为初始化程序不是 运行.

如何在创建新实例时让 FactoryBot 运行 after_initialize

谢谢。

(我这里可能用词有误,如有请指正。)

FactoryBot 为属性调用 setter,而不是构造函数,因此 build :some_model, some_attribute: 123, other_attribute: :foo 实际上等同于:

SomeModel.new.tap{|m|
  m.some_attribute = 123
  m.other_attribute = :foo
}

这种方法是在一个空对象上调用初始化程序,并且只有在这之后才会设置所有其他属性。

当您需要设置一些依赖于其他属性的属性时 - 我会选择像

这样的自定义设置器
def some_other_attribute=(val)
  self.some_attribute = calculate(val)
  super
end

或在 before_validation

中完成

您可以使用工厂的 after(:build) 回调来调用初始化程序,因为 factorybot 在初始化后设置属性。

FactoryBot.define do
  factory :some_model do
    some_number { 1 }
    is_it_something { false }
    account { create(:user).accounts.first }
    after(:build) do |some_model|
      some_model.initializer(some_model)
    end
  end
end

这样你就不必每次从工厂创建新对象时都调用它

总之,这个initializer方法看起来有点代码味。我不确定它有什么作用,但也许有更好的方法可以防止这个问题。