让!与Rspec中的let和之前的版本

时间:2018-10-04 10:19:29

标签: ruby rspec rspec-rails

在Rspec中,let使用惰性实例化,因此let(:foo) { create(...) }不会被初始化,直到有人调用它为止。通常这很好,因为仅在需要时才使用它,从而使rspec测试时间更快。

但是,有时您会遇到一个需要该变量但没有明确调用它的规范。因此,使用延迟实例化,规范将失败。

一个解决方案是一鸣惊人! let!(:foo) { create(...) }将强制初始化变量。

一些开发人员似乎对此很反对,并且更喜欢:

let(:foo) { create(...) } before do foo end 强制初始化。

这是有原因的吗?两种方法有什么区别?

2 个答案:

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

这会强制carsubject之前创建并可供使用:

let(:driver) { create(:driver) }
subject { driver.car() }

before { create(:car) }

it { expect(subject).to eq car } # Success