web-dev-qa-db-ja.com

RSpecには、「unstub」と同等のメソッドがありますが、「should_receive」用です。

RSpecの使用中にスタブやモックを削除する方法はありますか?

例:

RestClient.should_receive(:delete).with("http://www.example.com")
...
... 

# this will remove the mocking of "should_receive" and 
# restore the proper "delete" method on "RestClient".
RestClient.mocking_reset

mocking_resetは、必要な機能の架空の名前です)。

「スタブ」をリセットするが「should_receive」をリセットしない「unstub」メソッドがあることを知っています。

それで、「should_receive」のために「unstub」と同等のメソッドはありますか?

パナヨティス

39
p.matsinopoulos

現時点(rspec-mocks 2.10.1)では、unstubに相当するメソッドはありませんが、should_receive用のメソッドがあります。 rspec_resetを使用してすべてのスタブとモックをリセットできます。また、ダーティーハックを記述して特定の期待値を削除することもできます(これはお勧めしません)。

以下は、オブジェクトのすべてのスタブと期待値を削除する例です。

describe "resetting stubs and expectations with rspec_reset" do
  before do
    @person = mock('person')
    @person.should_receive(:poke)
  end

  it "should not fail when we reset all stubs and expectations" do
    @person.rspec_reset
  end
end

このメソッドはrspecソースコードで@privateと注釈されていることに注意してください。これは、絶対に必要以上に使用することは避け、警告なしにrspecの将来のバージョンで機能しなくなる可能性があることを意味します。ただし、rspec自体の仕様ではかなり広く使用されているため、すぐに廃止される可能性は低くなります。

Rspec-mocksコードをもう少し掘り下げた後は、もちろん非常に厄介なことをして、特定の期待を自分で引き裂くことができます。

# Works in rspec 2.10.1
describe "removing an expectation with an ugly hack" do
  before do
    @person = mock('person')
    @person.should_receive(:poke)
  end

  it "should not fail after we hack rspec by violating every law of good programming, ever" do
    @person.instance_variable_get(:@mock_proxy).instance_variable_get(:@method_double)[:poke].clear
  end
end

これはrspecテストパッケージのカプセル化に違反しているので、これは本当に悪いことです。代わりに、特定の期待値を削除する説得力のある理由が本当にある場合、正しい方法は、unstubに並列メソッドを追加するrspec-mocksの上流にパブリックメソッドを追加することですが、特定の期待値を削除します。それはおそらくここにあります(このファイルのstubunstubの定義にも注意してください):

https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/methods.rb

外出して、期待を取り除くための上記の提案のいずれかを使用する前に、仕様で本当にこれを行う必要があるのか​​、それともリファクタリングする必要があるのか​​を検討する必要があります。 should_receiveの使用はコードに関するアサーションであり、通常は1つのことだけをアサートする例(つまりitブロック)を作成する必要があります。グローバル設定で過度に実行しようとしない限り(例:rspec_resetまたはbefore:allブロック)、または例で試行している場合を除いて、before :eachのようなものが必要な理由について、私は非常に興味があります。やり過ぎる(つまり、1つの例で複数のアサーションを使用する)。

もちろん、これに対する1つの例外は、rspec-mocksのテストスイートにあります。ここで、rspec_resetは、スタブとモックを適用する機能をテストするサンプル間の状態をリセットするために使用されます。そうしていなければ、オブジェクトのスタブとモックのグローバルリセットに依存しないようにテストを改善できる可能性があります。

これがお役に立てば幸いです。unstubに同等のメソッドを追加する正当なケースがあると思われる場合は、メッセージの期待のために(つまり、should_receiveを使用した後)お知らせください。これが問題ないことだと確信している場合は、このメソッドを追加し、上流に追加することを提案することを検討します。多分それはunset_expectationと呼ばれるでしょうか?上記のコードと内部構造のガイダンスを自由に使用して、rspec-mocksのプルリクエストを自分でまとめてください。unstubに相当するモックを作成するために受け入れられるかどうか確認します。

30
Justin Leitgeb

次の方法で、以前のモックの一部を上書きできます。

expect(RestClient).to receive(:delete).and_call_original

または、それがどのような期待でもなかった場合は、単純なスタブのみを使用します。

allow(RestClient).to receive(:delete).and_call_original

expect_any_instance_ofallow_any_instance_ofも存在することに注意してください。

44

RSpec> = 2.14の場合、受け入れられた回答は機能しなくなります。このスレッドでベルンハルトケーラーの回答を参照します(リンクの腐敗を防ぐために以下に再現): rspecバージョン2.14の未定義のメソッド `rspec_reset '

ResetメソッドはRSpec 2.14には存在しません。代わりに、rspec-mocksプロジェクトのspec_helper.rbファイル内で定義されたヘルパーメソッドです。

module VerifyAndResetHelpers
  def verify(object)
    RSpec::Mocks.proxy_for(object).verify
  end

  def reset(object)
    RSpec::Mocks.proxy_for(object).reset
  end
end

このメソッドは、問題のオブジェクトのクラス定義にリセットアクションを追加するのではなく、基になるプロキシにリセットアクションを委任していることがわかります。

10
JellicleCat