【问题标题】:Problem with Factory_girl, association and after_initializeFactory_girl、关联和 after_initialize 的问题
【发布时间】:2011-05-06 19:32:07
【问题描述】:

我有一个这样定义的家庭类:

class Family < ActiveRecord::Base
  after_initialize :initialize_family
  belongs_to :user
  validates :user, 
       :presence => true

  validates :name,   
       :presence => true,          
       :length => { :maximum => 30 },
       :format => { :with => /\A[a-zA-Z0-9\-_\s\']+\z/i}

  def initialize_family
    if self.name.blank? && self.user
        self.name = "#{self.user.profile_full_name}'s Family"
    end
  end
end

在我的 factory.rb 中有:

Factory.define :family do |f|
   f.association :user, :factory => :user
end

在我的 family_spec.rb 中有

let(:family) { Factory(:family) }

但这失败了:

1) Family is valid with valid attributes
     Failure/Error: let(:family) { Factory(:family) }
     ActiveRecord::RecordInvalid:
       Validation failed: Name can't be blank, Name is invalid, Languages can't be blank, Languages is too short (minimum is 1 characters)
     # ./spec/models/family_spec.rb:8:in `block (2 levels) in <top (required)>'
     # ./spec/models/family_spec.rb:10:in `block (2 levels) in <top (required)>'

使用调试器我可以看到当调用 after_initialize 时 self.user 为 nil。为什么会这样? 如果我用 create 或 new 给家人打电话,一切正常。

感谢您的帮助。

【问题讨论】:

    标签: ruby-on-rails-3 factory-bot


    【解决方案1】:

    这是我从 Joe Ferris 那里得到的答案:

    factory_girl 不会将参数传递给构造函数。它使用#user= on 你的模型,并在没有任何参数的情况下实例化它。

    还有本休斯的这个:

    为了详细说明 Joe 所说的内容,调用 after_initialize 方法 在对象初始化后立即,并且那个时候确实用户没有 已设置。

    例如,虽然这会起作用:

    family = Family.create!(:user => @user) # or @user.families.create ...
    

    这不会(这是 factory_girl 在幕后所做的):

    family = Family.new
    family.user = @user
    family.save!
    

    一般来说,您希望在使用 after_initialize 时非常小心,因为 请记住,每个对象初始化都会调用它。一个 Family.all 通话 在 1,000 个对象上将导致它被调用 1,000 次。

    听起来在这种情况下你可能会更好地使用 before_validation 而不是 after_initialize。

    以下语法也适用于 rspec 中的测试:

    let (:family) { Family.create(:user => @user) }
    

    【讨论】:

    • 好的,这真的很有帮助。但它很困惑:*(抱歉英语不好
    • 不使用参数实例化新对象是否有任何好处?正如@GuiGS 下面所说,可以使用initialize_with,所以这当然不是 factory_girl 的设计限制。我想知道为什么initialize_with 的行为不是默认的?
    【解决方案2】:

    由于after_initialize在新对象被实例化后触发,而factory_girl默认通过调用new不带任何参数来构建实例,因此您必须使用initialize_with覆盖默认构建。

    FactoryGirl.define do
      factory :family do
        initialize_with { new(user: build(:user)) }
      end
    end
    

    【讨论】:

    • 虽然 Matteo 回答了原因,但这就是实际的方法。
    • 也可以这样称呼initialize_with { new(attributes) }
    • 这真的很有帮助!谢谢你:)
    【解决方案3】:

    我认为是因为关联是惰性的,因此在“after_initialize”中还没有用户。

    http://rdoc.info/github/thoughtbot/factory_girl/v1.3.3/file/README.rdoc

    也许你可以直接从另一个工厂调用一个工厂,但我没有尝试这个,例如

    f.user Factory(:user)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-13
      • 2013-07-10
      相关资源
      最近更新 更多