我已经阅读了RSpec manual关于差异的说法,但有些事情仍然令人困惑。其他所有来源,包括“RSpec Book”仅解释“let”,而“The Rails 3 Way”与手册一样令人困惑。
我理解“let”仅在调用时进行评估,并在范围内保持相同的值。所以有意义的是,在manual的第一个例子中,第一个测试通过,因为“let”只被调用一次,第二个测试通过,因为它增加了第一个测试的值(已经评估过一次)在第一次测试中,其值为1)。
之后,因为“让!”在定义时进行评估,并在被调用时再次进行,如果测试没有失败,因为“count.should eq(1)”应该是“count.should eq(2)”?
任何帮助都将不胜感激。
答案 0 :(得分:43)
我通过一个非常简单的例子理解了let
和let!
之间的区别。让我首先阅读文档句子,然后显示输出。
About let doc说: -
...
let
延迟评估:直到第一次才对其进行评估 调用它定义的方法。
我理解与以下示例的区别: -
$count = 0
describe "let" do
let(:count) { $count += 1 }
it "returns 1" do
expect($count).to eq(1)
end
end
让我们现在运行: -
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>
为什么错误?因为,正如doc所说,使用let
,,直到第一次调用它定义的方法时才会对其进行评估。在示例中,我们没有调用count
,$count
仍为0
,而不是1
增加。
现在来到let!
部分。医生说
....你可以使用let!在每个示例之前强制执行方法的调用。这意味着即使你没有在示例中调用 helper 方法,它仍会在你的例子运行之前被调用。
让我们测试一下: -
这是修改后的代码
$count = 0
describe "let!" do
let!(:count) { $count += 1 }
it "returns 1" do
expect($count).to eq(1)
end
end
让我们运行这段代码: -
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
请参阅,现在$count
返回1
,因此测试已通过。它发生在我使用let!
之前,它在示例运行之前运行,尽管我们没有在示例中调用count
。
这是let
和let!
之间的差异。
答案 1 :(得分:25)
您可以阅读有关此here的更多信息,但基本上可以。 (:let)
被懒惰地评估,如果你不调用它将永远不会被实例化,而(:let!)
在每个方法调用之前被强制评估。
答案 2 :(得分:11)
在定义时不会调用它,而是在每个示例之前调用(然后它被记忆化,而不是由示例再次调用)。这样,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
答案 3 :(得分:4)
我也认为这很令人困惑,但我认为The Rails 3 Way的例子很好 let类似于before块中的实例变量,而let!立即备忘录
来自The 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
“由于评论块永远不会被执行 断言如果使用了let定义,则只有一个注释 已添加到此规范中,即使实现可能正在运行。 通过使用让!我们确保创建初始评论和规范 现在会通过。“
答案 4 :(得分:2)
我也对let
和let!
感到困惑,所以我从here获取文档代码并使用它:
https://gist.github.com/3489451
希望它有所帮助!
答案 5 :(得分:1)
这是一种保持规格可预测的方法。
你应该总是使用let
。除非您有意在示例之间缓存值,否则不应使用let!
。这就是原因:
describe '#method' do
# this user persists in the db across all sub contexts
let!(:user) { create :user }
context 'scenario 1' do
context 'sub scenario' do
# ...
# 1000 lines long
# ...
end
context 'sub scenario' do
# you need to test user with a certain trait
# and you forgot someone else (or yourself) already has a user created
# with `let!` all the way on the top
let(:user) { create :user, :trait }
it 'fails even though you think it should pass' do
# this might not be the best example but I found this pattern
# pretty common in different code bases
# And your spec failed, and you scratch your head until you realize
# there are more users in the db than you like
# and you are just testing against a wrong user
expect(User.first.trait).to eq xxx
end
end
end
end