人们似乎已经解释了它们之间的一些基本差异,但遗漏了before(:all)
个,并且没有确切解释为什么应该使用它们.
我认为实例变量在绝大多数规范中都没有使用的位置,部分原因是this answer中提到的原因,所以我在这里不提它们.
let blocks
let
块中的代码仅在引用时执行,延迟加载这意味着这些块的顺序无关紧要.这给了你大量的权力,以减少重复设置通过您的规格.
其中一个(非常小且人为的)例子是:
let(:person) { build(:person) }
subject(:result) { Library.calculate_awesome(person, has_moustache) }
context 'with a moustache' do
let(:has_moustache) { true }
its(:awesome?) { should be_true }
end
context 'without a moustache' do
let(:has_moustache) { false }
its(:awesome?) { should be_false }
end
你可以看到has_moustache
在每种情况下都有不同的定义,但是没有必要重复subject
的定义.需要注意的是,将使用在当前上下文中定义的最后let
个块.这有助于设置用于大多数规格的默认值,如果需要,可以覆盖这些默认值.
例如,如果通过person
模型并将top_hat
设置为true,则判断返回值calculate_awesome
,但没有胡子:
context 'without a moustache but with a top hat' do
let(:has_moustache) { false }
let(:person) { build(:person, top_hat: true) }
its(:awesome?) { should be_true }
end
关于let块还有一点需要注意,如果您正在搜索已保存到数据库(即Library.find_awesome_people(search_criteria)
)中的内容,则不应使用它们,因为除非它们已被引用,否则它们不会保存到数据库中.这里应该使用let!
或before
个街区.
Also, never ever use before
to trigger execution of let
blocks, this is what let!
is made for!
let! blocks
按照定义的顺序执行let!
个块(很像before块).与before块的一个核心区别是,您得到了对这个变量的显式引用,而不需要返回到实例变量.
与let
个块一样,如果用相同的名称定义了多个let!
个块,则执行中将使用最新的块.核心区别在于,如果像这样使用,let!
个块将执行多次,而let
个块将只执行最后一次.
before(:each) blocks
before(:each)
是默认的before块,因此可以引用为before {}
,而不是每次指定完整的before(:each) {}
.
我个人倾向于在一些核心情况下使用before
个街区.如果出现以下情况,我将使用before blocks:
- 我用的是嘲弄、存根或替身
- 有任何合理大小的设置(通常这表明您的工厂没有正确设置)
- 有许多变量我不需要直接引用,但它们是设置所必需的
- 我正在rails中编写功能控制器测试,我想执行一个特定的测试请求(即
before { get :index }
).尽管在很多情况下你可以使用subject
,但如果你不需要参考资料,它有时会让你感觉更明确.
如果你发现自己为你的规格写了before
个大模块,判断你的工厂,确保你完全理解这些特性及其灵活性.
before(:all) blocks
在当前上下文(及其子上下文)中的规范之前,这些操作只执行一次.如果写得正确,这些可以发挥巨大的优势,因为在某些情况下,这可以减少执行和工作量.
一个例子(几乎不会影响执行时间)是为测试模拟一个ENV变量,您应该只需要做一次.
希望有帮助:)