ruby Rails - RSpec - “让”和“让!”之间的区别
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10173097/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rails - RSpec - Difference between "let" and "let!"
提问by Theo Scholiadis
I have read what the RSpec manualsays about the difference, but some things are still confusing. Every other source, including "The RSpec Book" only explain about "let", and "The Rails 3 Way" is just as confusing as the manual.
我已经阅读了RSpec 手册关于差异的内容,但有些事情仍然令人困惑。所有其他来源,包括“The RSpec Book”只解释了“let”,而“Rails 3 Way”和手册一样令人困惑。
I understand that "let" is only evaluated when invoked, and keeps the same value within a scope. So it makes sense that in the first example in the manualthe first test passes as the "let" is invoked only once, and the second test passes as it adds to the value of the first test (which was evaluated once in the first test and has the value of 1).
我知道“让”仅在调用时进行评估,并在范围内保持相同的值。因此,在手册中的第一个示例中,第一个测试通过,因为“let”仅被调用一次,而第二个测试通过添加到第一个测试的值(在第一个测试中评估一次)并具有 1) 的值。
Following that, since "let!" evaluates when defined, and again when invoked, should the test not fail as "count.should eq(1)" should have instead be "count.should eq(2)"?
之后,因为“让!” 定义时评估,再次调用时,测试是否应该不会失败,因为“count.should eq(1)”应该改为“count.should eq(2)”?
Any help would be appreciated.
任何帮助,将不胜感激。
采纳答案by dhoelzgen
It's not invoked when defined, but rather before each example (and then it's memoized and not invoked again by the example). This way, count will have a value of 1.
它不是在定义时调用,而是在每个示例之前调用(然后它被记住并且不会被示例再次调用)。这样,count 的值为 1。
Anyway, if you have another example, the before hook is invoked again - all of the following tests pass:
无论如何,如果您有另一个示例,则再次调用 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
回答by Arup Rakshit
I understood the difference between letand let!with a very simple example. Let me read the doc sentence first, then show the output hands on.
我通过一个非常简单的例子理解了let和之间的区别let!。让我先阅读文档句子,然后动手展示输出。
About letdoc says :-
关于让医生说:-
...
letis lazy-evaluated: it is not evaluated until the first time the method it defines is invoked.
...
let是惰性求值的:直到第一次调用它定义的方法时才会求值。
I understood the difference with the below example :-
我理解与以下示例的区别:-
$count = 0
describe "let" do
let(:count) { $count += 1 }
it "returns 1" do
expect($count).to eq(1)
end
end
Lets run it now :-
让我们现在运行它:-
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>
Why the ERROR? Because, as doc said, with let, it is not evaluated until the first time the method it defines is invoked.In the example, we didn't invoke the count, thus $countis still 0, not incremented by 1.
为什么是错误?因为,正如 doc 所说, with let,直到第一次调用它定义的方法时才会对其进行评估。在示例中,我们没有调用count,因此$count仍然是0,没有增加1。
Now coming to the part let!. The doc is saying
现在到了部分let!。医生说
....You can use let! to force the method's invocation before each example. It means even if you didn't invoke the helpermethod inside the example, still it will be invoked before your example runs.
....你可以使用让!在每个示例之前强制调用方法。这意味着即使您没有调用example 中的helper方法,它仍然会在您的 example 运行之前被调用。
Lets test this also :-
让我们也测试一下:-
Here is the modified code
这是修改后的代码
$count = 0
describe "let!" do
let!(:count) { $count += 1 }
it "returns 1" do
expect($count).to eq(1)
end
end
Lets run this code :-
让我们运行此代码:-
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
See, now $countreturns 1, thus test got passed. It happened as I used let!, which run before the example run, although we didn't invoke countinside our example.
看,现在$count返回1,因此测试通过了。它发生在我使用的let!,它在示例运行之前运行,尽管我们没有count在我们的示例中调用。
This is how letand let!differs from each other.
这就是彼此的方式let和let!不同之处。
回答by Justin Herrick
回答by cluv
I also thought this was confusing, but I think the examples from The Rails 3 Way are good.
let is analogous to instance variables in the before block whereas let! is memoized immediately
我也认为这很令人困惑,但我认为 The Rails 3 Way 中的示例很好。
let 类似于 before 块中的实例变量,而 let! 立即被记住
From The Rails 3 Way
从 Rails 3 路
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
"Since the comment block would never have been executed for the first assertion if you used a let definition, only one comment would have been added in this spec even though the implementation may be working. By using let! we ensure the initial comment gets created and the spec will now pass."
“因为如果您使用 let 定义,则永远不会为第一个断言执行注释块,即使实现可能正在运行,本规范中也只会添加一个注释。通过使用 let!我们确保创建初始注释现在规范将通过。”
回答by Jonathan Lin
I was also confused by letand let!, so I took the documentation code from hereand played with it:
https://gist.github.com/3489451
我也被letand弄糊涂了let!,所以我从这里拿了文档代码并玩了一下:https:
//gist.github.com/3489451
Hope it helps!
希望能帮助到你!
回答by Edmund Lee
And here's a way to keep your specs predictable.
这是一种使您的规格可预测的方法。
You should pretty much always use let. You should not use let!unless you intentionally want to cache the value across examples. This is why:
你应该几乎总是使用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

