ruby-on-rails - 何时使用rspec let( )?

  显示原文与译文双语对照的内容

我倾向于在块之前使用并设置实例变量,然后在示例中使用它们,但最近我在 let() 中出现。 根据rspec文档,它被用来

。定义 memoized helper 方法。 值将在同一示例的多个调用中缓存,但不在示例中。

我的问题是这与在块中使用实例变量有什么不同? 以及什么时候使用 let() vs before()

时间:

出于以下原因,我总是喜欢 let 到实例变量:

  • 引用时实例变量 spring 已经存在。 这意味着,如果你变胖手指拼写的实例变量,将创建并初始化一个新的nil, 这可能导致微妙的Bug 和假阳性。 因为 let 创建了一个方法,当你拼错它的时候,你会得到一个 NameError,我觉得更好。 它使得重构规范变得更加容易。
  • before(:each) 钩子将在每个示例之前运行,即使该示例不使用在钩子中定义的任何实例变量。 这通常不是一个大问题,但是如果实例变量的设置花费了很长时间,那么你就在浪费时间。 对于由 let 定义的方法,初始化代码仅在示例调用它时才运行。
  • 你可以在示例中直接从局部变量中进行重构,而不改变示例中的引用语法。 如果对实例变量进行重构,则必须更改引用对象的方式( 例如。 添加 @ ) 。
  • 这有点主观,但就像 Mike Lewis指出的,我认为这使得规范更易于阅读。 我喜欢使用 let 定义所有依赖对象的组织,并保持 it 块的美观和简短。

一般来说,let() 是一个更好的语法,它可以让你在整个地方输入 @name 符号。 但是,警告 emptor 我发现 let() 还引入了微妙的Bug ( 或者至少头部划伤) 因为变量不存在,直到你尝试使用它。 告诉故事的迹象:如果添加一个 putslet() 后看到变量是正确的规范允许通过,但未经规范 puts 失败--你发现这个微妙。

我还发现 let() 在所有情况下都不缓存 ! 我在博客中写了它: http://technicaldebt.com/?p=1242

也许只是我?

让我们以它的本质作为一个过程。 也缓存它的缓存。

我发现了一个让我立即发现的问题。。 在评估更改的规范块中。


let(:object) {FactoryGirl.create :object}

expect {
 post :destroy, id: review.id
}.to change(Object, :count).by(-1)

你需要确保在期望块之外调用。 换句话说,在你的允许块中调用 FactoryGirl.create 。 我通常通过验证对象的持久化来实现。


object.persisted?.should eq true

否则,当第一次调用时,由于延迟实例化,数据库中的更改会真正发生。

更新

只是添加一个音符。小心玩代码高尔夫游戏或者在这种情况下rspec高尔夫。

在这种情况下,我只需要调用对象响应的方法。 所以我调用 。保留? 对象上的方法作为它的truthy 。 我想做的只是实例化对象。 你可以打电话给空或者零? 同样,要点不是测试,而是通过调用它来使对象处于生命中。

所以你不能重构


object.persisted?.should eq true

要成为


object.should be_persisted 

由于对象还没有实例化。。 它的惰性:

更新2

利用让语法即时创建对象,应该完全避免这个问题。! 注意,尽管它会打败非banged的懒惰的许多目的。

另外,在某些情况下,你可能想要使用主题语法,而不是让它给你额外的选项。


subject(:object) {FactoryGirl.create :object}

注意对约瑟--如果创建数据库对象在 before(:all) 他们不会在一个事务中被捕获,你更有可能离开围绕在你的测试数据库。 使用 before(:each) 。

所使用的另一个原因让及其懒惰的评价如此你可以把一个复杂的对象和测试覆盖让各个部分的情况下,在这个非常做作的例子:


context"foo" do
 let(:params) do
 { :foo => foo, :bar =>"bar" }
 end
 let(:foo) {"foo" }
 it"is set to foo" do
 params[:foo].should eq("foo")
 end
 context"when foo is bar" do
 let(:foo) {"bar" }
 # NOTE we didn't have to redefine params entirely!
 it"is set to bar" do
 params[:foo].should eq("bar")
 end
 end
end

你可以叫你 let 年代 before(:all) 块运动懒惰的eval如果你不愿意使用 let, 将re-evaluate每个块在每个测试之前。!

...