私は RSpecマニュアル が違いについて述べていることを読みましたが、いくつかはまだ混乱しています。 「The RSpec Book」を含むその他すべてのソースは「let」についてのみ説明しており、「The Rails 3 Way」はマニュアルと同じくらい混乱しています。
「let」は呼び出されたときにのみ評価され、スコープ内で同じ値を保持することを理解しています。したがって、 manual の最初の例では、「let」が1回だけ呼び出されると最初のテストに合格し、2番目のテストは最初のテストの値(最初のテストで一度評価され、値は1)です。
続いて「let!」から「count.should eq(1)」が「count.should eq(2)」でなければならないので、定義時に評価され、呼び出されたときに再びテストが失敗しないか?
任意の助けいただければ幸いです。
定義時に呼び出されるのではなく、各例の前に呼び出されます(メモ化され、例によって再度呼び出されることはありません)。これにより、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
非常に単純な例でlet
とlet!
の違いを理解しました。最初にdoc文を読んでから、出力を実際に見てみましょう。
letについて docによると:-
...
let
is lazy-evaluated:定義されているメソッドが初めて呼び出されるまで評価されません。
私は以下の例との違いを理解しました:-
$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>
[〜#〜] error [〜#〜]なのはなぜですか?なぜなら、docが言ったように、let
ではそれが定義するメソッドが最初に呼び出されるまで評価されないからです。- example、count
を呼び出さなかったため、$count
は0
のままであり、1
によってインクリメントされていません。
パートlet!
に移動します。ドクは言っています
.... letを使用できます!各例の前にメソッドの呼び出しを強制します。 help]メソッドをexample内で呼び出さなくても、例が実行される前に呼び出されます。
これもテストしてみましょう:-
これが変更されたコードです
$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
を返すため、テストに合格しました。これは、サンプル内でcount
を呼び出さなかったものの、サンプルの実行前に実行されるlet!
を使用したときに発生しました。
これがlet
とlet!
の違いです。
あなたはこれについてもっと読むことができます ここ 、しかし基本的に。 (:let)
は遅延評価され、呼び出さない限りインスタンス化されませんが、(:let!)
は、各メソッド呼び出しの前に強制的に評価されます。
私もこれは混乱を招くと思いましたが、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
「let定義を使用した場合、最初のアサーションに対してコメントブロックは実行されなかったため、実装が機能している場合でも、この仕様にはコメントが1つだけ追加されます。letを使用することにより、最初のコメントが作成されるようにします。そして、スペックは合格するでしょう。」
let
とlet!
にも混乱していたので、ドキュメントコードを here から取得して、次のように操作しました: https://Gist.github。 com/3489451
それが役に立てば幸い!
そして、これがあなたのスペックを予測可能に保つ方法です。
ほとんど常に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