web-dev-qa-db-ja.com

RSpecのallow / expectと単にexpect / and_returnの比較

RSpec、具体的にはバージョン> = 3では、次の間に違いがあります:

  • allowを使用してテストダブルを返すパラメーターでメッセージの期待値を設定し、次にexpectを使用して返されたテストダブルでアサーションを作成します。
  • expectを使用して、パラメーターを使用して期待値を設定し、テストダブルを返します。

それとも、すべて意味論ですか? expectで戻り値を提供/指定することは RSpecモック2.13の構文 でしたが、私が見る限り RSpecモック3で変更された構文 を使用してallowを使用します。

ただし、以下の(渡す)サンプルコードでは、allow/expectまたはexpect/_and_return_のいずれかを使用しても同じ結果が生成されるようです。ある構文が別の構文よりも好まれた場合、おそらく何らかの非推奨通知があると予想していましたが、そうではないので、両方の構文が有効であると見なされるようです:

_class Foo
  def self.bar(baz)
    # not important what happens to baz parameter
    # only important that it is passed in
    new
  end

  def qux
    # perform some action
  end
end

class SomethingThatCallsFoo
  def some_long_process(baz)
    # do some processing
    Foo.bar(baz).qux
    # do other processing
  end
end

describe SomethingThatCallsFoo do
  let(:foo_caller) { SomethingThatCallsFoo.new }

  describe '#some_long_process' do
    let(:foobar_result) { double('foobar_result') }
    let(:baz) { double('baz') }

    context 'using allow/expect' do
      before do
        allow(Foo).to receive(:bar).with(baz).and_return(foobar_result)
      end

      it 'calls qux method on result of Foo.bar(baz)' do
        expect(foobar_result).to receive(:qux)
        foo_caller.some_long_process(baz)
      end
    end

    context 'using expect/and_return' do
      it 'calls qux method on result of Foo.bar(baz)' do
        expect(Foo).to receive(:bar).with(baz).and_return(foobar_result)
        expect(foobar_result).to receive(:qux)
        foo_caller.some_long_process(baz)
      end
    end
  end
end
_

渡されたbazパラメーターを別のテストダブルに変更して意図的にテストを失敗させた場合、エラーはほとんど同じです。

_  1) SomethingThatCallsFoo#some_long_process using allow/expect calls quux method on result of Foo.bar(baz)
     Failure/Error: Foo.bar(baz).qux
       <Foo (class)> received :bar with unexpected arguments
         expected: (#<RSpec::Mocks::Double:0x3fe97a0127fc @name="baz">)
              got: (#<RSpec::Mocks::Double:0x3fe97998540c @name=nil>)
        Please stub a default value first if message might be received with other args as well.
     # ./foo_test.rb:16:in `some_long_process'
     # ./foo_test.rb:35:in `block (4 levels) in <top (required)>'

  2) SomethingThatCallsFoo#some_long_process using expect/and_return calls quux method on result of Foo.bar(baz)
     Failure/Error: Foo.bar(baz).qux
       <Foo (class)> received :bar with unexpected arguments
         expected: (#<RSpec::Mocks::Double:0x3fe979935fd8 @name="baz">)
              got: (#<RSpec::Mocks::Double:0x3fe979cc5c0c @name=nil>)
     # ./foo_test.rb:16:in `some_long_process'
     # ./foo_test.rb:43:in `block (4 levels) in <top (required)>'
_

それで、結果または表現された意図のいずれかで、これらの2つのテスト間に実際の違いはありますか、それとも単にセマンティクスおよび/または個人的な好みですか? allow/expectは一般的にexpect/_and_return_を介して使用する必要があります。これは置換構文のように見えるか、またはそれぞれで特定のテストシナリオ?

更新

森の答え を読んだ後、上記のサンプルコードからFoo.bar(baz).qux行をコメントアウトし、次のエラーを受け取りました。

_  1) SomethingThatCallsFoo#some_long_process using allow/expect calls qux method on result of Foo.bar(baz)
     Failure/Error: expect(foobar_result).to receive(:qux)
       (Double "foobar_result").qux(any args)
           expected: 1 time with any arguments
           received: 0 times with any arguments
     # ./foo_test.rb:34:in `block (4 levels) in <top (required)>'

  2) SomethingThatCallsFoo#some_long_process using expect/and_return calls qux method on result of Foo.bar(baz)
     Failure/Error: expect(Foo).to receive(:bar).with(baz).and_return(foobar_result)
       (<Foo (class)>).bar(#<RSpec::Mocks::Double:0x3fc211944fa4 @name="baz">)
           expected: 1 time with arguments: (#<RSpec::Mocks::Double:0x3fc211944fa4 @name="baz">)
           received: 0 times
     # ./foo_test.rb:41:in `block (4 levels) in <top (required)>'
_
  • _foobar_result_ doubleはFoo.bar(baz)の結果に代わらないため、_#qux_が呼び出されることはないため、allow仕様は失敗します。
  • expect仕様はFoo.bar(baz)を受け取らない点で失敗するので、_foobar_result_ doubleを問い合わせる点にさえ到達しません

理にかなっています:構文の変更だけではなく、expect/_and_return_にはallow/expectとは異なる目的があります。 RSpec Mocks README の最も明白な場所、特に以下のセクションを確認する必要がありました。

30
Paul Fioravanti

従来の記事 Mocks Are n't Stubs を参照してください。 allowはスタブを作成し、expectはモックを作成します。つまり、allowは、オブジェクトがスタブなしで返すものの代わりにXを返すことを許可し、expectallowplus何らかの状態またはイベントの期待です。書くとき

_allow(Foo).to receive(:bar).with(baz).and_return(foobar_result)
_

... Fooで_foobar_result_を受け取ったときに_:bar_を返すようにbazを変更するように仕様環境に指示しています。しかし、あなたが書くとき

_expect(Foo).to receive(:bar).with(baz).and_return(foobar_result) 
_

...同じことをしていることに加えて、仕様に失敗するように指示しますnlessFoobazで_:bar_を受け取ります。

違いを確認するには、Foonot receive _:bar_ with bazを行う例で両方を試してください。

86
Mori