Stub unimplemented method in rspec

11,532

Solution 1

RSpec raises this exception because it is not useful to stub a method that does not exist on the original object.

Mocking methods is always error-prone because the mock might behave differently than the original implementation and therefore specs might be successful even if the original implementation would have returned an error (or does not even exist). Allowing to mock non-existing methods is just plain wrong.

Therefore I would argue that you should not try to bypass this exception. Just add a name method to your class that raises a clear exception if run outside of the test environment:

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

Solution 2

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

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

Solution 3

I think that if the test you're writing is focused on your MyModule module, and that module relies on an instance method in the class that it is mixed into, then I think that method should be mocked out in the anonymous class that you use when testing the module. For example:

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
Share:
11,532
Filip Bartuzi
Author by

Filip Bartuzi

RRR - Rails & React & Redux

Updated on June 13, 2022

Comments

  • Filip Bartuzi
    Filip Bartuzi almost 2 years

    I'm testing my module and I decided to test it versus anonymous class:

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

    MyModule uses method name inside klass. To let my specs work I need to stub this method name (which is unimplemented). So I wrote:

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

    but it raises:

     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>'
    

    how to stub this method without defining it?

  • Stefan
    Stefan over 8 years
    name should probably be an instance method returning 'SOreadytohelp'
  • spickermann
    spickermann over 8 years
    @Stefan: I agree, that is also a good idea. Depends on the focus: To stress that specs and code are in sync or that there is something to fix...
  • Nathan Gouy
    Nathan Gouy over 5 years
    definition of NotImplementedError : NotImplementedError Raised when a feature is not implemented on the current platform. For example, methods depending on the +fsync+ or +fork+ system calls may raise this exception if the underlying operating system or Ruby runtime does not support them. it should be NoMethodError : Raised when a method is called on a receiver which doesn’t have it defined and also fails to respond with +method_missing+ Correction needed