A Sale
принадлежит Item
и Price
.Item
принадлежит Item_Type
, а Price
также относится к ItemType
.Для любого Sale
s
у нас должно быть s.item.item_type == s.price.item_type
, иначе база данных находится в недопустимом состоянии.Кроме того, Sale validates_presence_of :item, :price
.
Я хотел бы построить Sale
экземпляров, используя FactoryBot.
FactoryBot.define do
sequence :item_type_name do |n|
"type#{n}"
end
factory :item_type do
name {generate(:item_type_name)}
end
factory :item
item_type
end
factory :price do
item_type
end
factory :sale do
item
price
end
end
sale = build(:sale)
Проблема: это приводит к продаже с конфликтующими item_types
sale.item.item_type != sale.price.item_type
Попытка исправить:
factory :sale do
item
price
after(:build) do |sale|
if (sale.item.item_type != sale.price.item_type)
sale.price = build(:price, item_type: sale.item.item_type)
end
end
end
Это работает нормально, когда вы строите продажу без переопределений
sale = build(:sale)
, но дает нежелательное поведение, когда предоставляется переопределение price
иitem
не равно:
myPrice = build(:price)
sale = build(:sale, price: myPrice) # oops! myPrice is overwritten
Требуемое поведение выглядит следующим образом
describe 'sale_factory' do
context 'no overrides provided' do
it 'makes an item and price with matching item_types' do
sale = build(:sale)
expect(sale.item.item_type).to eq sale.price.item_type
end
end
context 'item override provided' do
it 'makes a price whose item_type matches that of item override' do
item = build(:item)
sale = build(:sale, item: item)
expect(sale.item).to eq item
expect(sale.price.item_type).to eq item.item_type
end
context 'price override provided' do
it 'makes an item whose item_type matches that of price override' do
price = build(:price)
sale = build(:sale, price: price)
expect(sale.price).to eq price
expect(sale.item.item_type).to eq price.item_type
end
context 'price and item overrides provided' do
it 'makes a sale using provided overrides if item_types do not conflict' do
item = build(:item)
price = build(:price, item_type: item.item_type)
sale = build(:sale, item: item, price: price)
expect(sale.item).to eq item
expect(sale.price).to eq price
end
it 'raises an error if item_types conflict' do
item_type_1 = build(:item_type)
item_type_2 = build(:item_type)
item = build(:item, item_type: item_type_1)
price = build(:price, item_type: item_type_2)
expect{build(:sale, item: item, price: price)}
.to raise_error "conflicting item types"
end
end
end
Как мне добиться желаемого поведения?