http://betterspecs.org/#subject にはsubject
およびlet
に関する情報があります。ただし、それらの違いについてはまだ不明です。さらに、SO post RSpecテストでbefore、let、subjectを使用することに対する議論は何ですか? は、どちらもsubject
を使用しない方が良いと述べましたまたはlet
。どこに行こうか?とても混乱しています。
要約:RSpecのサブジェクトは、テストされるオブジェクトを参照する特別な変数です。期待は暗黙的に設定でき、1行の例をサポートします。いくつかの慣用的なケースでは読者には明らかですが、それ以外の点では理解が難しく、避けるべきです。 RSpecのlet
変数は、遅延してインスタンス化された(メモされた)変数です。彼らは主題として従うのは難しいことではありませんが、まだ絡み合ったテストにつながる可能性があるため、慎重に使用する必要があります。
サブジェクトはテスト対象のオブジェクトです。 RSpecには、主題に関する明確な考えがあります。定義されている場合と定義されていない場合があります。その場合、RSpecは明示的に参照せずにメソッドを呼び出すことができます。
デフォルトでは、最も外側の例のグループ(describe
またはcontext
ブロック)の最初の引数がクラスである場合、RSpecはそのクラスのインスタンスを作成し、サブジェクトに割り当てます。たとえば、次のパスは次のとおりです。
class A
end
describe A do
it "is instantiated by RSpec" do
expect(subject).to be_an(A)
end
end
subject
を使用して、サブジェクトを自分で定義できます。
describe "anonymous subject" do
subject { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end
サブジェクトを定義するときに、サブジェクトに名前を付けることができます。
describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(a).to be_an(A)
end
end
サブジェクトに名前を付けても、匿名で参照できます。
describe "named subject" do
subject(:a) { A.new }
it "has been instantiated" do
expect(subject).to be_an(A)
end
end
複数の名前付きサブジェクトを定義できます。最後に定義された名前付きサブジェクトは、匿名のsubject
です。
ただし、件名は定義されていますが、
遅延的にインスタンス化されます。つまり、記述されたクラスの暗黙的なインスタンス化またはsubject
に渡されたブロックの実行は、例でsubject
または名前付きサブジェクトが参照されるまで発生しません。明示的なサブジェクトを(グループ内の例を実行する前に)熱心にインスタンス化する場合は、subject
ではなくsubject!
と言います。
期待値を暗黙的に設定できます(subject
または名前付きサブジェクトの名前を書くことなく):
describe A do
it { is_expected.to be_an(A) }
end
件名は、この1行の構文をサポートするために存在します。
暗黙のsubject
(例のグループから推測)は理解するのが難しい
is_expected
を呼び出すことによって)使用されるか、明示的に(subject
として)使用されるかにかかわらず、期待が呼び出されるオブジェクトの役割や性質に関する情報を読者に提供しません。it
への文字列引数)がないため、読者がサンプルの目的に関して持っている情報は、期待そのものだけです。したがって、コンテキストがすべての読者によく理解される可能性が高く、実際にサンプルの説明が必要ない場合にのみ、暗黙のサブジェクトを使用すると役立ちます。正規のケースは、shouldマッチャーでActiveRecord検証をテストすることです。
describe Article do
it { is_expected.to validate_presence_of(:title) }
end
明示的な匿名subject
(名前のないsubject
で定義されている)は、読者がインスタンス化された方法を見ることができるため、少し優れていますが、
名前付きサブジェクトは意図を明らかにする名前を提供しますが、let
変数の代わりに名前付きサブジェクトを使用する唯一の理由は、匿名サブジェクトを時々使用したい場合であり、匿名サブジェクトが理解しにくい理由を説明しただけです。
したがって、明示的な匿名subject
または名前付きサブジェクトの正当な使用は非常にまれです。
let
変数let
変数は、次の2つの違いを除いて、名前付きサブジェクトとまったく同じです。
let
/let!
の代わりにsubject
/subject!
で定義されていますsubject
を設定したり、暗黙的に暗黙的な呼び出しを期待したりすることはできません。例の重複を減らすためにlet
を使用することは完全に正当です。ただし、テストの明確さを犠牲にしない場合にのみ行ってください。let
を使用する最も安全な時期は、let
変数の目的が(読者が各例を理解するために何行も離れている可能性がある定義を見つける必要がないように)、すべての例で同じように使用されます。これらのいずれかが当てはまらない場合は、例でオブジェクトをプレーンな古いローカル変数で定義するか、ファクトリメソッドを呼び出すことを検討してください。
let!
は怠riskではないため危険です。誰かがlet!
を含むサンプルグループにサンプルを追加したが、そのサンプルがlet!
変数を必要としない場合、
let!
変数を見て、それが例にどのように影響するのか、そしてどのように影響するのか疑問に思うので、その例は理解するのが難しいでしょう。let!
variablleの作成に時間がかかるため、この例は必要以上に遅くなります。したがって、let!
を使用するのは、たとえそうであったとしても、将来のサンプルライターがそのthatに陥る可能性が低い、小規模で単純なサンプルグループでのみです。
サブジェクトまたはlet
変数の一般的な乱用があり、個別に説明する価値があります。一部の人々は、次のようにそれらを使用したいです:
describe 'Calculator' do
describe '#calculate' do
subject { Calculator.calculate }
it { is_expected.to be >= 0 }
it { is_expected.to be <= 9 }
end
end
(これは、2つの期待値が必要な数値を返すメソッドの簡単な例ですが、このスタイルは、メソッドが多くの期待値を必要とする、および/または多くの副作用を持つより複雑な値を返す場合、より多くの例/期待値を持つことができますすべてに期待が必要です。)
これは、例ごとに1つの期待値(サンプルごとに1つのメソッド呼び出しのみをテストする必要がある有効な規則と混同される)が必要だということを聞いたため、またはRSpecのトリッキーさが大好きだからです。匿名または名前付きの件名、またはlet
変数を使用して、それをしないでください!このスタイルにはいくつかの問題があります。
代わりに、単一の例を記述します。
describe 'Calculator' do
describe '#calculate' do
it "returns a single-digit number" do
result = Calculator.calculate
expect(result).to be >= 0
expect(result).to be <= 9
end
end
end
Subject
とlet
は、テストを整頓してスピードアップするための単なるツールです。 rspecコミュニティーの人々はそれらを使用するので、それらを使用しても大丈夫かどうかは心配しません。それらは同様に使用できますが、わずかに異なる目的に役立ちます
Subject
を使用すると、テストサブジェクトを宣言し、後で任意の数の次のテストケースに再利用できます。これにより、コードの繰り返しが減少します(コードの乾燥)
Let
は、テストデータをインスタンス変数に割り当てるbefore: each
ブロックの代替です。 Let
にはいくつかの利点があります。まず、インスタンス変数に値を割り当てずに値をキャッシュします。第二に、それは遅延評価されます。つまり、仕様が要求するまで評価されません。したがって、let
を使用すると、テストを高速化できます。 let
は読みやすいと思います
subject
はテスト対象であり、通常はインスタンスまたはクラスです。 let
は、テスト内の変数を割り当てるためのものであり、インスタンス変数を使用して遅延評価されます。このスレッドにはいくつかの素敵な例があります。