在rspec中存根未实现的方法

时间:2015-09-02 09:30:21

标签: ruby-on-rails ruby ruby-on-rails-4 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>'

如何在不定义的情况下存根此方法?

3 个答案:

答案 0 :(得分:5)

RSpec引发了这个异常,因为对原始对象上不存在的方法进行存根是没有用的。

模拟方法总是容易出错,因为模拟的行为可能与原始实现不同,因此即使原始实现返回错误(或者甚至不存在),规范也可能成功。允许模拟不存在的方法是完全错误的。

因此我认为你不应该试图绕过这个例外。只需在您的类中添加name方法,如果在测试环境之外运行,则会引发明确的异常:

def self.name
  raise NoMethodError  # TODO: check specs...
end

答案 1 :(得分:3)

subject(:klass) do 
  Struct.new(:name) do
   include MyModule
  end
end

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

答案 2 :(得分:1)

我认为如果你正在编写的测试集中在你的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