RSpecのlet
ブロックとbefore
ブロックの違いは何ですか?
そして、それぞれをいつ使用するのですか?
以下の例で良いアプローチは何ですか(前または前)?
let(:user) { User.make !}
let(:account) {user.account.make!}
before(:each) do
@user = User.make!
@account = @user.account.make!
end
私はこれを研究しました stackoverflow post
しかし、上記のような関連のものにletを定義するのは良いことですか?
人々は、それらが異なる基本的な方法のいくつかを説明したように見えますが、before(:all)
を省き、それらが使用されるべき理由を正確に説明しません。
this answer に記載されている理由もあり、インスタンス変数は大部分の仕様で使用されている場所がないと考えているため、ここではオプションとして言及しません。
letブロック
let
ブロック内のコードは、参照されたときにのみ実行されます。これを遅延ロードすると、これらのブロックの順序は無関係になります。これにより、スペックを介して繰り返しセットアップを削減するための大量の電力が提供されます。
この1つの(非常に小さくて不自然な)例は次のとおりです。
_let(:person) { build(:person) }
subject(:result) { Library.calculate_awesome(person, has_moustache) }
context 'with a moustache' do
let(:has_moustache) { true }
its(:awesome?) { should be_true }
end
context 'without a moustache' do
let(:has_moustache) { false }
its(:awesome?) { should be_false }
end
_
_has_moustache
_がそれぞれの場合で異なるように定義されていることがわかりますが、subject
の定義を繰り返す必要はありません。重要なことは、現在のコンテキストで定義されている最後のlet
ブロックが使用されることです。これは、大部分の仕様で使用されるデフォルトを設定するのに適しています。必要に応じて上書きできます。
たとえば、_calculate_awesome
_をtrueに設定してperson
モデルを渡した場合、_top_hat
_の戻り値をチェックしますが、口ひげはありません。
_context 'without a moustache but with a top hat' do
let(:has_moustache) { false }
let(:person) { build(:person, top_hat: true) }
its(:awesome?) { should be_true }
end
_
Letブロックについて注意すべきもう1つの点は、データベースに保存されているもの(つまり、Library.find_awesome_people(search_criteria)
)を検索する場合は使用しないでください。参照されました。 _let!
_またはbefore
ブロックはここで使用されるべきものです。
また、nevereverbefore
を使用してlet
ブロックの実行をトリガーします、これが_let!
_の目的です!
let!ブロック
_let!
_ブロックは、定義された順序で実行されます(beforeブロックのように)。 beforeブロックとの主な違いは、インスタンス変数にフォールバックする必要がなく、この変数への明示的な参照を取得することです。
let
ブロックと同様に、複数の_let!
_ブロックが同じ名前で定義されている場合、最新のものが実行時に使用されます。コアの違いは、_let!
_ブロックがこのように使用されると複数回実行されるのに対し、let
ブロックは最後にのみ実行されることです。
before(:each)ブロック
before(:each)
はブロック前のデフォルトであるため、毎回完全なbefore(:each) {}
を指定するのではなく、_before {}
_として参照できます。
いくつかのコアな状況でbefore
ブロックを使用することが私の個人的な好みです。次の場合、ブロックの前に使用します。
before { get :index }
_)。多くの場合、これにsubject
を使用できますが、参照が不要な場合は、より明確に感じることがあります。仕様のために大きなbefore
ブロックを記述していることに気付いた場合は、工場を調べて、特性とその柔軟性を完全に理解していることを確認してください。
before(:all)ブロック
これらは、現在のコンテキスト(およびその子)の仕様の前に一度だけ実行されます。これらは実行と労力を削減できる特定の状況があるため、正しく記述されていれば非常に有利に使用できます。
1つの例(実行時間にほとんど影響しない)は、テストのためにENV変数をモックアウトすることです。これは一度だけ実行する必要があります。
それが役立つことを願っています:)
ほとんどの場合、let
を好みます。リンクする投稿は、let
も高速であることを指定しています。ただし、多くのコマンドを実行する必要がある場合は、before(:each)
を使用できます。これは、多くのコマンドが関係する場合に構文がより明確になるためです。
あなたの例では、before(:each)
の代わりにlet
を使用することをお勧めします。一般的に、いくつかの変数の初期化が完了すると、let
を使用する傾向があります。
言及されていない大きな違いは、let
で定義された変数は、最初に呼び出すまでインスタンス化されないということです。 before(:each)
ブロックはすべての変数をインスタンス化しますが、let
は複数のテストで使用する可能性のあるいくつかの変数を定義できますが、自動的にはインスタンス化しません。これを知らずに、すべてのデータが事前にロードされることを期待している場合、テストは戻って自分に噛み付く可能性があります。場合によっては、いくつかのlet
変数を定義し、before(:each)
ブロックを使用して各let
インスタンスを呼び出して、データが使用可能であることを確認することもできます。そもそも.
Machinistを使用しているようです。 makeにいくつかの問題があることに注意してください!グローバルなフィクスチャトランザクションの外部で発生するlet(非ビッグバージョン)の内部(トランザクションフィクスチャも使用している場合)で、他のテストのデータが破損します。