Rails - RSpec - "let"和"let!"之间的区别

The*_*dis 39 ruby rspec rspec2

我已经阅读了RSpec手册中关于差异的内容,但有些事情仍然令人困惑.其他所有来源,包括"RSpec Book"仅解释"let",而"The Rails 3 Way"与手册一样令人困惑.

我理解"let"仅在调用时进行评估,并在范围内保持相同的值.所以有意义的是,在手册的第一个例子中,第一个测试通过,因为"let"只调用一次,第二个测试通过,因为它增加了第一个测试的值(在第一个测试中评估了一次)并具有1)的值.

之后,因为"让!" 在定义时进行评估,并在被调用时再次进行,如果测试没有失败,因为"count.should eq(1)"应该是"count.should eq(2)"?

任何帮助,将不胜感激.

Aru*_*hit 45

我理解了一个非常简单的例子let和之间的区别let!.让我先阅读文档句子,然后显示输出动画.

关于让 doc说: -

... let惰性求值的:直到第一次调用它定义的方法时才会对它进行求值.

我理解与以下例子的不同之处: -

$count = 0
describe "let" do
  let(:count) { $count += 1 }

  it "returns 1" do
    expect($count).to eq(1)
  end
end
Run Code Online (Sandbox Code Playgroud)

让我们现在运行: -

arup@linux-wzza:~/Ruby> rspec spec/test_spec.rb
F

Failures:

  1) let is not cached across examples
     Failure/Error: expect($count).to eq(1)

       expected: 1
            got: 0

       (compared using ==)
     # ./spec/test_spec.rb:8:in `block (2 levels) in <top (required)>'

Finished in 0.00138 seconds (files took 0.13618 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/test_spec.rb:7 # let is not cached across examples
arup@linux-wzza:~/Ruby>
Run Code Online (Sandbox Code Playgroud)

为何错误?因为,如DOC表示,与let,它不是评估在第一次定义了方法被调用,直到.在这个例子中,我们没有调用count,因此$count仍然0没有增加1.

现在来到这一部分let!.医生说

....你可以用let!在每个示例之前强制执行方法的调用.这意味着即使您没有在示例中调用辅助方法,仍然会在示例运行之前调用它.

让我们测试一下: -

这是修改后的代码

$count = 0
describe "let!" do
  let!(:count) { $count += 1 }

  it "returns 1" do
    expect($count).to eq(1)
  end
end
Run Code Online (Sandbox Code Playgroud)

让我们运行这段代码: -

arup@linux-wzza:~/Ruby> rspec spec/test_spec.rb
.

Finished in 0.00145 seconds (files took 0.13458 seconds to load)
1 example, 0 failures
Run Code Online (Sandbox Code Playgroud)

看,现在$count返回1,因此测试通过了.它发生在我使用的let!,它在示例运行之前运行,尽管我们没有count在我们的示例中调用.

这是如何letlet!互不相同.

  • 请记住,永远不要在块之前的 a 块中放置 let 块,这就是 let !是为。有关更多信息,请查看此页面:https://kolosek.com/rspec-let-vs-before (2认同)

Jus*_*ick 26

你可以在这里阅读更多相关内容,但基本上.(:let)是懒惰的评估,如果你不调用它将永远不会被实例化,而(:let!)在每次方法调用之前强制评估.

  • Halle-flippin-lujah,终于有一个与记忆无关的解释了。简单明了,切中要害,一切都应该如此。 (2认同)

dho*_*gen 14

它在定义时不会被调用,而是在每个示例之前调用(然后它被记忆化,而不是由示例再次调用).这样,count的值为1.

无论如何,如果你有另一个例子,再次调用before钩子 - 所有以下测试都通过:

$count = 0
describe "let!" do
  invocation_order = []

  let!(:count) do
    invocation_order << :let!
    $count += 1
  end

  it "calls the helper method in a before hook" do
    invocation_order << :example
    invocation_order.should == [:let!, :example]
    count.should eq(1)
  end

  it "calls the helper method again" do
    count.should eq(2)
  end
end
Run Code Online (Sandbox Code Playgroud)

  • 不,它已被调用和记忆,因此$ count不再增加.无论如何,如果你有另一个例子,再次调用before钩子.看到我编辑的答案,我添加了一些代码以便澄清. (3认同)
  • 这是备忘录,而不是记忆.来自Obie Fernandez的书"Memoized意味着与let相关联的代码块被执行一次并存储以供将来调用,从而提高性能." (2认同)

clu*_*luv 5

我也认为这令人困惑,但是我认为The Rails 3 Way中的示例很好。
let类似于before块中的实例变量,而let!立即被记住

从Rails 3 Way

describe BlogPost do
  let(:blog_post) { BlogPost.create :title => 'Hello' }
  let!(:comment) { blog_post.comments.create :text => 'first post' }

  describe "#comment" do
    before do
     blog_post.comment("finally got a first post")
    end

    it "adds the comment" do
      blog_post.comments.count.should == 2
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

“由于如果使用let定义,因为注释块永远不会对第一个断言执行,因此即使实现可行,在本规范中也只会添加一个注释。通过使用let!我们确保创建了初始注释规格将通过。”