在RSPEC中,Let的行为是在单个示例(它阻止)中进行记忆,但在某些情况下,这可能会导致一些潜在的令人讨厌的副作用。
我注意到如果你设法尝试创建任何被认为是昂贵的东西,例如大型模拟,那么整个对象创建将重复它所调用的每一个例子。
对此进行故障排除的第一步是将模拟数据破解为大小,这将大部分运行时间从~30秒减少到~0.08秒。通过将一个被调用3次以上且没有任何形式突变的let变量传递给实例,速度可以进一步提高(在这种情况下为-0.02到-0.04)。
通常,可以推测懒惰的评估是可取的,并且在某些情况下这样的事情是安全的代价。在大型测试套件(3000+测试)的背景下,甚至0.01-0.02秒的差异通常足以导致20-30秒的膨胀。当然,在某些情况下,这是任意编号,但你可以看出为什么这是不合需要的并且会产生复合问题。
我的问题是:
感谢您的时间!
答案 0 :(得分:3)
您似乎意识到,let
基本上只是让您无法评估变量,除非您使用它的示例。如果您在十个示例中使用它,那么您确实会在昂贵的操作中获得十次点击。
因此,对于您的第一个问题,我不知道我能提供有用的答案。这是非常情境化的,但是如果你经常使用变量并且这是一个昂贵的操作,我会说let
是不可行的。但根据您的需求,它可能仍然是最佳选择 - 也许您必须在大多数示例中重置状态,但不是全部。在这种情况下,操作的费用可能不值得尝试在少数情况下分享它。
对于你的第二个问题,我想说尝试让let
在一个区块内工作可能不是一个好主意。这是before(:all)
块和实例变量的情况。
你的第三个问题是我认为真正的肉在哪里,所以请耐心等待。
FactoryGirl实际上不会改变你的问题。它将构建并可选地保存对象,但您仍需要决定在何处以及如何使用它。如果您开始将其弹出到before(:each)
块中,或者在大多数示例中调用构建器,您仍然会有性能命中。
根据您的需要,您可以在before(:all)
块甚至before(:suite)
块中执行昂贵的操作(例如,在spec_helper.rb
中配置)。这样做的好处是可以减少对昂贵操作的点击次数,但缺点是如果您正在修改数据,则会对所有其他测试进行修改。这显然会导致许多难以调试的问题。如果您的数据需要通过多个示例进行更改,然后重置为原始状态,那么您将遇到某种性能损失或自己设计的自定义逻辑。
如果您的数据主要位于ActiveRecord对象中,并且您并不热衷于存根/模拟以防止访问数据库,那么您可能会遇到缓慢的测试问题。 Fixtures可以与事务一起使用,并且可以比工厂更快,但根据您的数据库架构,关系等可能很难维护。我相信您可以在before(:suite)
块中使用工厂,然后交易仍然有效,但这并不一定比固定装置更容易维护。
如果您的数据只是CPU资源而不是数据库记录,那么您可以设置一堆对象并通过Marshal模块对它们进行序列化。然后你可以在一个let
块中加载它们,预先建好并准备就绪,只需要一个磁盘命中(或者内存,如果你将Marshalled字符串存储在内存中):
# In irb or pry or even spec_helper.rb
object = SomeComplexThing.new
object.prepare_it_with_expensive_method_call_fun
Marshal.dump(object) # Store the output of this somewhere
# In some_spec.rb
let(:thing) { Marshal.load(IO.read("serialized_thing")) }
这样做的好处是可以完全序列化对象的状态,并在不重新计算昂贵数据的情况下完全恢复它。对于像ActiveRecord模型这样的非常复杂的对象,这可能不会有效,但它对于您自己设计的更简单的数据结构非常方便。您甚至可以通过实施marshal_dump
和marshal_load
方法来实现自己的转储/加载逻辑(请参阅我上面链接的Marshal文档),这可以在测试之外使用。
如果您的数据足够简单,您甚至可以使用以下设置:
# In spec_helper.rb
RSpec.configure do |config|
config.before(:suite) do
@object = SomeComplexThing.new
@object.prepare_it_with_expensive_method_call_fun
end
end
# In a test
let(:thing) { @object.dup }
这并不一定适用于所有情况,因为dup
是一个浅层副本(有关详细信息,请参阅the Ruby docs),但您明白了 - 您正在构建副本而不是而不是重新计算任何昂贵的东西会伤害你。
我希望这些信息有所帮助,因为我不确定我是否完全理解你需要的信息。