web-dev-qa-db-ja.com

RSpecのsubjectとletの違いは何ですか?それらはいつ使用されるべきですか?

http://betterspecs.org/#subject にはsubjectおよびletに関する情報があります。ただし、それらの違いについてはまだ不明です。さらに、SO post RSpecテストでbefore、let、subjectを使用することに対する議論は何ですか? は、どちらもsubjectを使用しない方が良いと述べましたまたはlet。どこに行こうか?とても混乱しています。

58
new2cpp

要約: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です。

ただし、件名は定義されていますが、

  1. 遅延的にインスタンス化されます。つまり、記述されたクラスの暗黙的なインスタンス化またはsubjectに渡されたブロックの実行は、例でsubjectまたは名前付きサブジェクトが参照されるまで発生しません。明示的なサブジェクトを(グループ内の例を実行する前に)熱心にインスタンス化する場合は、subjectではなくsubject!と言います。

  2. 期待値を暗黙的に設定できます(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変数を使用して、それをしないでください!このスタイルにはいくつかの問題があります。

  • 匿名の件名は例の件名ではありません—methodが件名です。この方法でテストを記述すると、言語が台無しになり、考えるのが難しくなります。
  • いつものように、1行の例のように、期待の意味を説明する余地はありません。
  • 件名は各例ごとに作成する必要があり、時間がかかります。

代わりに、単一の例を記述します。

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
120

Subjectletは、テストを整頓してスピードアップするための単なるツールです。 rspecコミュニティーの人々はそれらを使用するので、それらを使用しても大丈夫かどうかは心配しません。それらは同様に使用できますが、わずかに異なる目的に役立ちます

Subjectを使用すると、テストサブジェクトを宣言し、後で任意の数の次のテストケースに再利用できます。これにより、コードの繰り返しが減少します(コードの乾燥)

Letは、テストデータをインスタンス変数に割り当てるbefore: eachブロックの代替です。 Letにはいくつかの利点があります。まず、インスタンス変数に値を割り当てずに値をキャッシュします。第二に、それは遅延評価されます。つまり、仕様が要求するまで評価されません。したがって、letを使用すると、テストを高速化できます。 letは読みやすいと思います

3
Ren

subjectはテスト対象であり、通常はインスタンスまたはクラスです。 letは、テスト内の変数を割り当てるためのものであり、インスタンス変数を使用して遅延評価されます。このスレッドにはいくつかの素敵な例があります。

https://github.com/reachlocal/rspec-style-guide/issues/6

1
nikkypx