我倾向于在块以前使用来设置实例变量。 而后,我在个人示例中使用这些变量。 我最近遇到了let()
。 根据RSpec文档,它习惯了 数据库
...定义一个memoized帮助方法。 该值将在同一示例中的多个调用之间缓存,但不跨示例缓存。 api
这与在块以前使用实例变量有什么不一样? 还有何时你应该使用let()
vs before()
? 缓存
默认状况下,“before”表示before(:each)
。 Ref The Rspec Book,copyright 2010,page 228。 app
before(scope = :each, options={}, &block)
我使用before(:each)
为每一个示例组播种一些数据,而没必要调用let
方法在“it”块中建立数据。 在这种状况下,“it”块中的代码较少。 post
若是我想在某些示例中使用某些数据而不是其余示例,则使用let
。 测试
以前和以前都很适合干掉“它”块。 spa
为避免混淆,“let”与before(:all)
。 “让”从新评估每一个示例(“it”)的方法和值,可是在同一个示例中将值缓存到多个调用中。 你能够在这里阅读更多相关信息: https : //www.relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-let code
let是功能性的,由于它本质上是一个Proc。 也是它的缓存。 对象
我获得的一个问题是......在一个正在评估变化的Spec块中。 事务
let(:object) {FactoryGirl.create :object} expect { post :destroy, id: review.id }.to change(Object, :count).by(-1)
你须要必定要打电话let
你想到块以外。 即你在let块中调用FactoryGirl.create
。 我一般经过验证对象是否持久来作到这一点。
object.persisted?.should eq true
不然,当第一次调用let
块时,因为延迟实例化而实际发生数据库更改。
更新
只需添加备注。 当心打码高尔夫或在这种状况下rspec高尔夫与这个答案。
在这种状况下,我只须要调用一个对象响应的方法。 因此我调用_.persisted?
_对象的方法做为它的真实。 我要作的就是实例化对象。 你能够打电话给空吗? 仍是没有? 太。 关键不在于测试,而是经过调用它来引导生命。
因此你没法重构
object.persisted?.should eq true
成为
object.should be_persisted
由于对象还没有实例化...它的懒惰。 :)
更新2
利用让! 即时对象建立的语法 ,应该彻底避免这个问题。 注意虽然它会战胜不少非撞击让懒惰的目的。
此外,在某些状况下,您可能实际上想要利用主题语法而不是let,由于它可能会为您提供其余选项。
subject(:object) {FactoryGirl.create :object}
约瑟夫的注意事项 - 若是您在before(:all)
建立数据库对象,它们将不会在事务中捕获,而且您更有可能在测试数据库中留下残余物。 使用before(:each)
代替。
使用let及其懒惰评估的另外一个缘由是你能够经过覆盖上下文中的let来获取一个复杂的对象并测试单个部分,就像在这个很是人为的例子中同样:
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
来使用上下文在个人API规范中测试个人HTTP 404响应。
要建立资源,我使用let!
。 可是为了存储资源标识符,我使用let
。 看看它的样子:
let!(:country) { create(:country) } let(:country_id) { country.id } before { get "api/countries/#{country_id}" } it 'responds with HTTP 200' { should respond_with(200) } context 'when the country does not exist' do let(:country_id) { -1 } it 'responds with HTTP 404' { should respond_with(404) } end
这使规格清晰可读。
使用实例变量和let()
之间的区别在于let()
是惰性求值的 。 这意味着在第一次运行它定义的方法以前,不会评估let()
。
before
和let
之间的区别在于let()
为您提供了一种以“级联”样式定义一组变量的好方法。 经过这样作,经过简化代码,规范看起来更好一些。