RSpec中let
和before
块之间的区别是什么?
什么时候使用?
以下示例中有什么好方法(让或之前)?
let(:user) { User.make !}
let(:account) {user.account.make!}
before(:each) do
@user = User.make!
@account = @user.account.make!
end
我研究了这个stackoverflow post
但是为上面的关联内容定义let是否合适?
答案 0 :(得分:133)
人们似乎已经解释了它们不同的一些基本方法,但遗漏了before(:all)
并且没有解释为什么应该使用它们。
我认为实例变量在绝大多数规范中都没有使用,部分原因是this answer中提到的原因,所以我不会在这里提及它们。
让阻止
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
块。这适用于设置大多数规范使用的默认值,如果需要可以覆盖。
例如,检查calculate_awesome
的返回值,如果传递person
模型,top_hat
设置为true,但没有胡子:
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
块是应该在这里使用的。
此外,从不永远 使用before
来触发let
块的执行,这就是let!
的目的!
让!块强>
let!
块按照它们的定义顺序执行(很像前一个块)。与块之前的一个核心差异是,您获得了对此变量的显式引用,而不是需要回退到实例变量。
与let
块一样,如果使用相同的名称定义多个let!
块,则最新的块将在执行时使用。如果像这样使用,则let!
块的核心区别将被执行多次,而let
块只会执行最后一次。
之前(:每个)阻止
before(:each)
是阻止前的默认值,因此可以引用为before {}
,而不是每次都指定完整的before(:each) {}
。
我个人倾向于在少数核心情况下使用before
块。我会在块之前使用if:
before { get :index }
)。即使您在很多情况下可以使用subject
,但如果您不需要参考,有时会感觉更明确。如果您发现自己为规范编写了大型before
块,请检查您的工厂并确保您完全了解特征及其灵活性。
之前(:所有)阻止
这些只会在当前上下文(及其子项)中的规范之前执行一次。如果正确编写,这些可以非常有用,因为在某些情况下这可以减少执行和工作量。
一个例子(根本不会影响执行时间)是为测试模拟一个ENV变量,你应该只需要做一次。
希望有所帮助:)
答案 1 :(得分:25)
几乎总是,我更喜欢let
。您链接的帖子指定let
也更快。但是,有时,当必须执行许多命令时,我可以使用before(:each)
,因为当涉及许多命令时,它的语法更清晰。
在您的示例中,我绝对更愿意使用let
而不是before(:each)
。一般来说,当只进行一些变量初始化时,我倾向于使用let
。
答案 2 :(得分:15)
未提及的一个重大差异是,在您第一次调用之前,用let
定义的变量不会被实例化。因此,虽然before(:each)
块将实例化所有变量,let
让您定义可能在多个测试中使用的多个变量,但它不会自动实例化它们。如果您希望预先加载所有数据,那么在不知道这一点的情况下,您的测试可能会回过头来咬自己。在某些情况下,您甚至可能希望定义多个let
变量,然后使用before(:each)
块来调用每个let
实例,以确保数据可用。
答案 3 :(得分:3)
看起来你正在使用机械师。请注意,您可能会看到make的一些问题! let(非爆炸版本)内部发生在全局夹具事务之外(如果您也使用事务夹具),因此破坏了其他测试的数据。