在Rspec中,let使用惰性实例化,因此let(:foo) { create(...) }
不会被初始化,直到有人调用它为止。通常这很好,因为仅在需要时才使用它,从而使rspec测试时间更快。
但是,有时您会遇到一个需要该变量但没有明确调用它的规范。因此,使用延迟实例化,规范将失败。
一个解决方案是一鸣惊人! let!(:foo) { create(...) }
将强制初始化变量。
一些开发人员似乎对此很反对,并且更喜欢:
let(:foo) { create(...) }
before do
foo
end
强制初始化。
这是有原因的吗?两种方法有什么区别?
答案 0 :(得分:0)
我可以想到一个区别:before
块会复合,您可以用let!
覆盖let
,反之亦然。我举一个例子:
context do
let!(:foo) { create(:foo) }
let(:bar) { create(:bar) }
before { bar }
context do
# if in this context you wish to switch back to "lazy" versions you can
# do that for :foo, just do:
let(:foo) { create(:foo) }
# but you can't "undo" before, even if you define an empty one:
before { }
# it does not cancel the `before` blocks defined in higher contexts
end
end
编辑:我只是意识到这并没有真正回答为什么有人会更喜欢before
而不是let!
的问题。也许:正如评论中提到的那样,顺序是不同的,但是如果您在规格中依赖于这种细微差别,那就太复杂了。
答案 1 :(得分:0)
许多情况只是样式问题,开发人员并不完全了解RSpec的主要功能,而且很多时候人们都没有意义。人不是机器,特别是在时间压力下,开发人员做了在理想条件下不会做的事情:)。
但是这两种情况并不完全相同。
例如,如果您使用的是subject
,则会在before
初始化之前在let!
挂钩中对其进行评估,而不是在it
内部进行评估。我没有测试,但是我相信这些情况应该显示出差异:
let!(:car) { create(:car) }
let(:driver) { create(:driver) }
subject { driver.car() }
it { expect(subject).to eq car } # Fail:
这会强制car
在subject
之前创建并可供使用:
let(:driver) { create(:driver) }
subject { driver.car() }
before { create(:car) }
it { expect(subject).to eq car } # Success