1. ホーム
  2. ruby-on-rails

[解決済み] RSpec: letとbeforeブロックの違いは何ですか?

2023-03-26 20:16:22

質問

とはどのような違いがあるのでしょうか? letbefore のブロックをRSpecで作成することはできますか?

そして、それぞれをいつ使うか?

以下の例では、どのようなアプローチ(letまたはbefore)が良いのでしょうか?

let(:user) { User.make !}
let(:account) {user.account.make!}

before(:each) do
 @user = User.make!
 @account = @user.account.make!
end

私はこれを勉強しました スタックオーバーフローの投稿

しかし、上記のような関連付けのためにletを定義するのは良いことなのでしょうか?

どのように解決するのですか?

人は、基本的な違いについて説明しているようですが、省かれているのは before(:all) を省き、なぜそれらを使うべきかを正確に説明していないようです。

で述べたような理由もあり、インスタンス変数は大部分の仕様で使用する場所がないと私は考えています。 この回答 で述べた理由もあり、ここでは選択肢として言及しません。

ブロック化する

の中のコード 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 を渡された場合 person を持つモデル top_hat を true に設定しても、口ひげはないでしょう。

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 ブロックについてもう一つ注意すべきことは、データベースに保存されているものを検索する場合には使用しないことです (すなわち、let ブロックは Library.find_awesome_people(search_criteria) のように) データベースに保存されているものを検索する場合には、 使ってはいけません。なぜなら、すでに参照されていない限り、 データベースには保存されないからです。 let! または before のようなブロックが使用されます。

また は決して 決して 使用 before の実行をトリガーするために let ブロックの実行をトリガするために、このような let! はそのために作られたのです!

ブロック

let! ブロックは定義された順に実行されます (before ブロックとほぼ同じです)。beforeブロックとの違いは、インスタンス変数にフォールバックする必要がなく、この変数への明示的な参照を得ることができる点です。

と同様に let ブロックと同様に、複数の let! ブロックが定義されている場合、最新のものが実行時に使用されます。主な違いは let! ブロックは複数回実行されます。 let ブロックは最後の一回だけ実行されます。

before(:each) ブロック

before(:each) はデフォルトのbeforeブロックであるため、以下のように参照することができます。 before {} と指定するのではなく、完全な before(:each) {} を毎回指定する必要はありません。

個人的な好みとして before ブロックを使用するのは、いくつかのコアな状況においてです。私は、もしもの時はbeforeブロックを使用します。

  • モッキング、スタブ、またはダブルを使用している。
  • 合理的な大きさの設定がある (一般にこれはファクトリーの特性が正しく設定されていないサインです)
  • 直接参照する必要はないが、セットアップに必要な変数がいくつかある。
  • 私はrailsで機能的なコントローラのテストを書いていて、テストするために特定のリクエストを実行したいです(つまり before { get :index } ). を使うことができるにもかかわらず subject を使うこともできますが、参照を必要としない方がより明示的に感じられることがあります。

もしあなたが大きな before ブロックを書いている場合は、ファクトリーを確認し、trait とその柔軟性を完全に理解していることを確認してください。

before(:all)ブロック

これらは、現在のコンテキスト(とその子)のspecの前に、一度だけ実行されます。これは、実行と労力を削減できる特定の状況があるため、正しく書かれた場合、大きな利点に使用することができます。

1つの例(実行時間にほとんど影響しません)は、テストのためにENV変数をモック化することです。

お役に立てれば幸いです :)