2014-01-21 70 views
2

我目前正在一个项目上工作,我想用工厂女孩创建测试,但我无法使它与多态has_many关联工作。我已经尝试了其他文章中提到的许多不同的可能性,但它仍然不起作用。我的模型是这样的:工厂女孩与has_many和has_one的多态关联

class Restaurant < ActiveRecord::Base 
    has_one :address, as: :addressable, dependent: :destroy 
    has_many :contacts, as: :contactable, dependent: :destroy 

    accepts_nested_attributes_for :contacts, allow_destroy: true 
    accepts_nested_attributes_for :address, allow_destroy: true 

    validates :name, presence: true 
    #validates :address, presence: true 
    #validates :contacts, presence: true 
end 

class Address < ActiveRecord::Base 
    belongs_to :addressable, polymorphic: true 

    # other unimportant validations, address is created valid, the problem is not here 
end 

class Contact < ActiveRecord::Base 
    belongs_to :contactable, polymorphic: true 
    # validations ommitted, contacts are created valid 
end 

所以bassically我想创建一个工厂与地址和联系方式餐厅(与餐厅验证的存在,但如果它是不可能的,即使没有他们),但我不能这样做。最后的语法应该是这样的:

let(:restaurant) { FactoryGirl.create(:restaurant) } 

也应建立相关的地址和联系方式。我阅读过很多文章,但总是遇到某种错误。目前我厂(序列正确定义)是这样的:

factory :restaurant do 
    name 
    # address {FactoryGirl.create(:address, addressable: aaa)} 
    # contacts {FactoryGirl.create_list(:contact,4, contactable: aaa)} 
    # Validations are off, so this callback is possible 
    after(:create) do |rest| 
    # Rest has ID here 
    rest.address = create(:restaurant_address, addressable: rest) 
    rest.contacts = create_list(:contact,4, contactable: rest) 
    end 
end 

factory :restaurant_address, class: Address do 
    # other attributes filled from sequences... 

    association :addressable, factory: :restaurant 
    # addressable factory: restaurant 
    # association(:restaurant) 
end 

factory :contact do 
    contact_type 
    value 

    association :contactable, :factory => :restaurant 
end 

有没有一种方法来创建与地址和联系人测试一个命令餐厅设置?我真的需要摆脱我的验证,因为后(:创建)回调? 当前状态如下:

  1. 餐厅是使用名称和ID创建的。
  2. 比正在创建的地址 - 所有正确的,它具有包括addressable_id和addressable_type的所有值
  3. 之后,所有联系人正在创建,再次一切都很好,cntacts具有正确的值。
  4. 之后,餐厅没有任何关联对象的ID,没有关联地址或联系人
  5. 之后,由于某种原因餐厅再次建立(也许添加这些关联?),并且它失败了:我得到ActiveRecord的:RecordInvalid。

我使用的是factory_girl_rails 4.3.0,ruby 1.9.3和rails 4.0.1。我会很乐意提供任何帮助。

更新1:

只是为了澄清我的目标,我希望能够使用一个命令来创建我的规格餐厅和能够访问相关的地址和联系方式,这应该在创建创建的餐厅。让我们忽略所有的验证(我从一开始就让他们在我的例子中注释掉了)。当我在(:build)之后使用时,会创建第一家餐厅,地址是用餐厅ID创建的,地址为addressable_id,类名称为addressable_type。一切都是正确的。问题是,餐厅不知道他们(它没有地址或联系人的ID),我无法从我想要的餐厅访问它们。

回答

7

真正彻底搜查后,我已经找到了答案here - stackoverflow question。这也回答指向这个gist。主要的是在after(:build)回调中建立关联,然后将它们保存在(:create)回调之后。所以它看起来像这样:

factory :restaurant do 
    name 

    trait :confirmed do 
    state 1 
    end 

    after(:build) do |restaurant| 
    restaurant.address = build(:restaurant_address, addressable: restaurant) 
    restaurant.contacts = build_list(:contact,4, contactable: restaurant) 
    end 

    after(:create) do |restaurant| 
    restaurant.contacts.each { |contact| contact.save! } 
    restaurant.address.save! 
    end 
end 

我也有我的RSpec的一个漏洞,因为我是用前(:每个)的回调,而不是之前(:所有)。我希望这个解决方案可以帮助某人。

1

的问题

验证的行的相关列表的长度是一个很难在SQL到框架的问题,所以这是一个困难的问题在ActiveRecord的帧为好。

如果要在地址表中存储餐馆外键,则实际上无法创建具有地址的餐馆,因为您需要保存餐馆以获取其主键。你可以在ActiveRecord中解决这个问题,方法是在内存中建立关联的对象,对这些对象进行验证,然后在一个SQL事务中提交整个对象图。

怎么做你问

什么可以解决此通过搬东西到after(:build)挂钩,而不是after(:create)普遍得到。 ActiveRecord将自行保存后保存其相关的has_onehas_many关联。

您现在遇到错误,因为您无法修改对象以满足after(:create)块中的验证,因为验证在回调运行时已经运行。

你可以改变你的餐厅工厂是这个样子:

factory :restaurant do 
    name 

    after(:build) do |restaurant| 
    restaurant.address = build(:restaurant_address, addressable: nil) 
    restaurant.contacts = build(:contact, 4, contactable: nil) 
    end 
end 

nil那儿是打破工厂之间的循环关系。如果这样做,则不能对addressable_idcontactable_id密钥进行验证,因为在保存restaurant之前它们将不可用。

替代

虽然你可以得到的ActiveRecord和FactoryGirl都做你的要求,它建立的依赖这是很难理解,并有可能导致泄漏的验证或意外的危险名单像现在看到的错误。

如果您以此方式从餐厅模型验证联系人是因为您创建了餐厅及其相应联系人的表单,您可以通过创建一个代表该表单的新对象ActiveModel来节省很多痛苦。您可以收集每个对象所需的属性,移动一些验证(尤其是验证联系人列表长度的验证),然后在该表单上创建对象图,这样做更加清晰和不太可能打破。

这还有一个额外的好处,就是在其他测试中轻松创建轻量级的restaurant对象,这些对象不需要担心联系人或地址。如果您每次强制工厂创建这些相关对象,您将很快遇到两个问题:

  • 您的测试将会非常缓慢。每次想要与餐厅合作时创建五条从属记录的规模都不会太大。
  • 如果您想要在测试中指定不同的联系人或地址,您将不断与工厂发生冲突。
+0

谢谢您的时间,我已经更新了我的问题,请忽略所有的验证,我的目标就是要创建一个地址和联系方式的餐厅,应在规范餐饮访问。试图设置无可寻址地址没有帮助,地址和联系人刚创建时没有餐厅的身份证,因此该协会被打破得更多。 – Giron