[英]Factory Girl with polymorphic association for has_many and has_one
我目前正在開發一個項目,我想用工廠女孩創建測試,但我無法使用多態has_many關聯。 我嘗試了其他文章中提到的許多不同的可能性,但它仍然無效。 我的模型看起來像這樣:
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
因此,我想要為餐廳創建一個地址和聯系人的工廠(在餐廳進行驗證,但如果不可能,即使沒有它們),但我無法這樣做。 最終語法應該是:
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
有沒有辦法在地址和聯系人設置的測試中創建一個帶有一個命令的餐館? 因為after(:create)回調,我真的需要擺脫我的驗證嗎? 現狀如下:
我正在使用factory_girl_rails 4.3.0,ruby 1.9.3和rails 4.0.1。 我會很高興得到任何幫助。
更新1:
為了澄清我的目標,我希望能夠使用一個命令在我的規范中創建餐廳,並能夠訪問相關的地址和聯系人,這應該在創建餐廳時創建。 讓我們忽略所有的驗證(我從一開始就在我的例子中注釋了它們)。 當我使用after(:build)后 ,創建第一個餐館,而不是使用餐館ID作為addressable_id和類名作為addressable_type創建地址。 同樣適用於聯系人,一切都是正確的。 問題是,餐廳不知道他們(它沒有地址ID或聯系人),我無法從我想要的餐廳訪問它們。
經過徹底的搜索后,我在這里找到了答案- stackoverflow問題 。這個答案也指向了這個要點 。 主要是在after(:build)回調中構建關聯,然后將它們保存在after(: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中也有一個錯誤,因為我之前使用(:每個)回調而不是之前(:all)。 我希望這個解決方案有助於某人。
問題
驗證相關行列表的長度是SQL中框架的難題,因此在ActiveRecord中構建框架也是一個難題。
如果您在地址表上存儲餐館外鍵,您實際上無法創建一個在保存時具有地址的餐館,因為您需要保存餐館以獲取其主鍵。 您可以通過在內存中構建關聯對象,驗證這些問題,然后在一個SQL事務中提交整個對象圖,來解決ActiveRecord中的這個問題。
怎么做你要問的
你通常可以通過將事物移動到after(:build)
鈎子而不是after(:create)
。 一旦保存自己,ActiveRecord將保存其依賴的has_one
和has_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_id
或contactable_id
鍵進行驗證,因為在保存restaurant
之前它們將無法使用。
備擇方案
雖然您可以同時使用ActiveRecord和FactoryGirl來執行您所要求的操作,但它會設置一個不穩定的依賴項列表,這些依賴項很難理解,並且很可能導致漏洞驗證或意外錯誤,例如您現在看到的錯誤。
如果您通過這種方式從餐館模型驗證聯系人,因為您創建餐館及其相應聯系人的表單,您可以通過創建一個新的ActiveModel
對象來表示該表單,從而為您節省很多痛苦。 您可以收集每個對象所需的屬性,移動一些驗證(尤其是驗證聯系人列表長度的驗證),然后以更清晰,更不可能的方式在該表單上創建對象圖。打破。
這樣做的另一個好處是可以輕松地在其他測試中創建輕量級restaurant
對象,而無需擔心聯系人或地址。 如果強制工廠每次都創建這些依賴對象,那么很快就會遇到兩個問題:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.