45

RSpecマニュアルに違いについて書かれていることを読みましたが、まだ混乱していることがいくつかあります。「TheRSpecBook」を含む他のすべての情報源は、「let」についてのみ説明しており、「The Rails 3 Way」は、マニュアルと同じように混乱しています。

「let」は呼び出されたときにのみ評価され、スコープ内で同じ値を保持することを理解しています。したがって、マニュアルの最初の例では、「let」が1回だけ呼び出されると最初のテストに合格し、最初のテスト(最初のテストで1回評価された)の値に追加されると2番目のテストに合格することは理にかなっています。値は1)です。

続いて、「レット!」定義されたときに評価し、再度呼び出されたときに、「count.should eq(1)」が代わりに「count.shouldeq(2)」である必要があるため、テストが失敗しないようにする必要がありますか?

どんな助けでもいただければ幸いです。

4

6 に答える 6

62

との違いを非常に簡単な例で理解しましletlet!。最初にドキュメントの文を読んでから、出力を実際に見てみましょう。

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を使うことができます!各例の前にメソッドの呼び出しを強制します。これは、サンプル内でヘルパーメソッドを呼び出さなかった場合でも、サンプルが実行される前に呼び出されることを意味します。

これもテストしましょう:-

これが変更されたコードです

$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!、互いに異なります。

于 2014-06-30T18:23:44.500 に答える
29

これについて詳しくはこちらをご覧くださいが、基本的には。(:let)は遅延評価され、呼び出さないとインスタンス化されませんが、(:let!)各メソッド呼び出しの前に強制的に評価されます。

于 2012-04-16T15:25:54.270 に答える
15

定義時に呼び出されるのではなく、各例の前に呼び出されます(その後、メモ化され、例によって再度呼び出されることはありません)。このように、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
于 2012-04-16T15:29:52.253 に答える
5

これも紛らわしいと思いましたが、The Rails3Wayの例は良いと思います。
letはbeforeブロックのインスタンス変数に類似していますが、let!すぐにメモ化されます

レール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

「let定義を使用した場合、最初のアサーションに対してコメントブロックが実行されることはなかったため、実装が機能していても、この仕様には1つのコメントしか追加されませんでした。let!を使用することで、最初のコメントが確実に作成されます。スペックは合格します。」

于 2013-02-04T22:25:52.983 に答える
2

私もとに混乱したletので、ここlet!からドキュメントコードを取得して、それで遊んだ: https ://gist.github.com/3489451

それが役に立てば幸い!

于 2012-08-28T03:44:37.270 に答える
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
于 2018-01-31T21:43:26.117 に答える