RSpec:let和a block之间有什么区别?

时间:2011-05-12 06:50:32

标签: ruby-on-rails unit-testing rspec

RSpec中letbefore块之间的区别是什么?

什么时候使用?

以下示例中有什么好方法(让或之前)?

let(:user) { User.make !}
let(:account) {user.account.make!}

before(:each) do
 @user = User.make!
 @account = @user.account.make!
end

我研究了这个stackoverflow post

但是为上面的关联内容定义let是否合适?

4 个答案:

答案 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:

  • 我正在使用模拟,存根或双打
  • 有任何合理大小的设置(通常这是您的工厂特性未正确设置的标志)
  • 有许多变量我不需要直接引用,但需要进行设置
  • 我在rails中编写功能控制器测试,我想执行一个特定的测试请求(即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(非爆炸版本)内部发生在全局夹具事务之外(如果您也使用事务夹具),因此破坏了其他测试的数据。