web-dev-qa-db-ja.com

rspecのスタブ未実装メソッド

モジュールをテストしていて、匿名クラスと比較してテストすることにしました。

  subject(:klass) { Class.new { include MyModule } }

MyModuleは、name内でメソッドklassを使用します。仕様を機能させるには、このメソッドname(実装されていません)をスタブ化する必要があります。だから私は書いた:

subject { klass.new }
allow(subject).to receive(:name).and_return('SOreadytohelp') }

しかし、それは発生します:

 RSpec::Mocks::MockExpectationError: #<#<Class:0x007feb67a17750>:0x007feb67c7adf8> does not implement: name
from spec-support-3.3.0/lib/rspec/support.rb:86:in `block in <module:Support>'

このメソッドを定義せずにスタブする方法は?

12
Filip Bartuzi

元のオブジェクトに存在しないメソッドをスタブ化することは役に立たないため、RSpecはこの例外を発生させます。

モックは元の実装とは異なる動作をする可能性があるため、モックメソッドは常にエラーが発生しやすく、元の実装がエラーを返した(または存在しなかった)場合でも仕様は成功する可能性があります。存在しないメソッドをモックすることを許可することは、まったく間違っています。

したがって、この例外を回避しようとすべきではないと私は主張します。 nameメソッドをクラスに追加するだけで、テスト環境の外部で実行すると明確な例外が発生します。

def self.name
  raise NoMethodError  # TODO: check specs...
end
6
spickermann
subject(:klass) do 
  Struct.new(:name) do
   include MyModule
  end
end

http://Ruby-doc.org/core-2.2.0/Struct.html

3
max

あなたが書いているテストがあなたのMyModuleモジュールに焦点を合わせていて、そのモジュールがそれが混合されているクラスのインスタンスメソッドに依存しているなら、私はそのメソッドがモックアウトされるべきだと思いますモジュールのテスト時に使用する匿名クラス。例えば:

module MyModule
  def call_name
    # expected implementation of #name to be
    # in the class this module is mixed into
    name
  end
end

RSpec.describe MyModule do
  let(:my_module_able) do
    Class.new do
      include MyModule

      # We don't care what the return value of this method is;
      # we just need this anonymous class to respond to #name
      def name
        'Some Name that is not SOReadytohelp'
      end
    end.new
  end

  describe '#call_name' do
    let(:name) { 'SOReadytohelp' }

    before do
      allow(my_module_able).to receive(:name).and_return(name)
    end

    it 'returns the name' do
      expect(my_module_able.call_name).to eq(name)
    end
  end
end
1
Paul Fioravanti