TL;博士
FactoryGirl试图通过做一个非常大的假设来提供帮助
不幸的是,ActiveRecord用这个来决定是否应该这样做
请try 将RSpec存根/模拟填充到FactoryGirl工厂.
RSpec模拟只能在规范的某些部分使用
如果你看一下将RSpec纳入say的文档
这里有几个选项:
不要使用FactoryGirl创建存根;使用存根库
如果想在FactoryGirl中保留模型属性逻辑,那没关系.
stub_data = attributes_for(:order)
stub_data[:line_items] = Array.new(5){
double(LineItem, attributes_for(:line_item))
}
order_stub = double(Order, stub_data)
是的,您必须手动创建关联.这不是坏事,
清除id
号区域
after(:stub) do |order, evaluator|
order.id = nil
order.line_items = build_stubbed_list(
:line_item,
evaluator.line_items_count,
order: order
)
end
创建自己对new_record?
的定义
factory :order do
ignore do
line_items_count 1
new_record true
end
after(:stub) do |order, evaluator|
order.define_singleton_method(:new_record?) do
evaluator.new_record
end
order.line_items = build_stubbed_list(
:line_item,
evaluator.line_items_count,
order: order
)
end
end
这是怎么回事?
在我看来,try 创建一个"存根"has_many
通常不是一个好主意
女孩,我们需要了解的是什么
- 数据库持久层/gem(即
ActiveRecord
、Mongoid
、,
- 任何存根/模拟库(mintest/mocks、rspec、mocha等)
- 模拟/存根的用途
数据库持久层
每个数据库持久层的行为都不同.事实上,很多人都表现得很好
Assumption:我猜你在剩下的时间里用的是ActiveRecord
在我写这篇文章时,ActiveRecord
的当前GA版本是4.1.0.什么时候
这在较旧的AR版本中也略有不同.这在中国是非常不同的
你可能会想:"但我可以用存根设置反向"
FactoryGirl.define do
factory :line_item do
association :order, factory: :order, strategy: :stub
end
end
li = build_stubbed(:line_item)
没错.虽然只是因为AR决定了not to
现在,你可能会想:"我完全可以在has_many
个对象中添加对象,而无需
order = Order.new
li = order.line_items.build(name: 'test')
puts LineItem.count # => 0
puts Order.count # => 0
puts order.line_items.size # => 1
li = LineItem.new(name: 'bar')
order.line_items << li
puts LineItem.count # => 0
puts Order.count # => 0
puts order.line_items.size # => 2
li = LineItem.new(name: 'foo')
order.line_items.concat(li)
puts LineItem.count # => 0
puts Order.count # => 0
puts order.line_items.size # => 3
order = Order.new
order.line_items = Array.new(5){ |n| LineItem.new(name: "test#{n}") }
puts LineItem.count # => 0
puts Order.count # => 0
puts order.line_items.size # => 5
是的,但这里order.line_items
真的是一个
FactoryGirl在这里真正能做的就是让底层阶级的行为
FactoryGirl确实try 在保存对象方面提供一些帮助.这主要是
...[a factory]首先保存关联,这样外键就可以正确地
等待你可能已经注意到,在上面的例子中,我遗漏了以下内容:
order = Order.new
order.line_items = Array.new(5){ |n| LineItem.new(name: "test#{n}") }
puts LineItem.count # => 0
puts Order.count # => 0
puts order.line_items.size # => 5
没错.我们可以将order.line_items=
设为一个数组,但它不是
存根/模拟库
有许多不同的类型,FactoryGirl都与之合作.为什么?
记住,在test library of choice中添加FactoryGirl语法.
如果FactoryGirl没有使用你喜欢的库,它在做什么?
模拟/存根的用途
在我们讨论引擎盖下的细节之前,我们需要定义what
Stubs对考试期间拨打的电话提供固定答案,通常不提供
这与"模仿"有着微妙的区别:
Mocks...: 预先编程的对象,其期望值构成
存根可以作为一种建立合作者的方式,提供固定的回应.坚持
无需任何存根即可轻松创建自己的库:
stubbed_object = Object.new
stubbed_object.define_singleton_method(:name) { 'Stubbly' }
stubbed_object.define_singleton_method(:quantity) { 123 }
stubbed_object.name # => 'Stubbly'
stubbed_object.quantity # => 123
因为FactoryGirl对图书馆完全不可知
查看FactoryGirl v.4.4.0的实现,我们可以看到
persisted?
new_record?
save
destroy
connection
reload
update_attribute
update_column
created_at
这些都是非常活跃的.但是,正如你在has_many
中看到的,
Why does the 100 association not work with the FactoryGirl stub?
如上所述,ActiveRecord判断其状态以决定是否应该
def new_record?
id.nil?
end
在我抛出一些修正之前,我想回到stub
的定义:
存根为测试过程中拨打的电话提供固定答案,通常为not
responding at all to anything outside what's programmed in for the test个.
FactoryGirl对存根的实现违反了这一原则.因为它没有
修正1:不要使用FactoryGirl创建存根
如果您希望创建/使用存根,请使用专用于该任务的库.自从
你甚至可以推出自己的超级简单存根工厂(是的,存在问题)
require 'ostruct'
def create_stub(stubbed_attributes)
OpenStruct.new(stubbed_attributes)
end
FactoryGirl使创建100个模型对象变得非常容易,而实际上
此外,正如您所注意到的,FactoryGirl的"存根"抽象有点复杂
如果想在FactoryGirl中保留模型属性逻辑,那没关系.
stub_data = attributes_for(:order)
stub_data[:line_items] = Array.new(5){
double(LineItem, attributes_for(:line_item))
}
order_stub = double(Order, stub_data)
是的,您必须手动设置关联.虽然你只会设置
有一个真正的存根库有助于明确说明这一点.
同样的道理也适用于那些被称为单一对象的长链方法,
Fix #2: 清除id
号区域
这更像是黑客行为.我们知道默认存根设置为id
.因此,我们
after(:stub) do |order, evaluator|
order.id = nil
order.line_items = build_stubbed_list(
:line_item,
evaluator.line_items_count,
order: order
)
end
我们永远不会有一个存根返回id
并设置has_many
Fix #3: 创建自己对new_record?
的定义
在这里,我们将id
的概念与存根是
module SettableNewRecord
def new_record?
@new_record
end
def new_record=(state)
@new_record = !!state
end
end
factory :order do
ignore do
line_items_count 1
new_record true
end
after(:stub) do |order, evaluator|
order.singleton_class.prepend(SettableNewRecord)
order.new_record = evaluator.new_record
order.line_items = build_stubbed_list(
:line_item,
evaluator.line_items_count,
order: order
)
end
end
我们仍然需要 for each 模型手动添加它.