Rails rspec undefined method `receive' for #<RSpec::ExampleGroups::

2.1k views Asked by At

I want to test that some instance is not receiving a specific method call. Trying to

RSpec.describe 'something' do
  before { subject.stubs(foo: 'bar') }
  it { is_expected.to_not receive(:foo) }
end

gives me

Failure/Error: it { is_expected.to_not receive(:foo) }

NoMethodError:
  undefined method `receive' for #<RSpec::ExampleGroups::Something:0x0000000010391588>

and trying to

RSpec.describe 'something else' do
  before { subject.stubs(foo: 42) }
  it { subject.expects(:foo).never; subject.foo }
end

passes. What am I doing wrong?

I was directed to the current not-working solution by Rails rspec undefined method `receive_message' for #<RSpec::ExampleGroups:: and I am using

RSpec 3.7
  - rspec-core 3.7.1
  - rspec-expectations 3.7.0
  - rspec-mocks 3.7.0
  - rspec-support 3.7.1

Update 1: I am using webmock instead of rspec-mocks.

Update 2: I am using webmock and mocha.

Update 3: Thanks for all the explanations, @engineersmnky! Unfortunately, for my specific use case, I still need to stub the method. Why is this one still passing?

require 'rspec'
require 'mocha'

RSpec.configure do |config|
  config.mock_with :mocha
end

RSpec.describe 'something' do
  # passes
  it 'can hide from foo' do 
    subject.stubs(:foo)
    expects(:foo).never
    subject.foo
  end
end

The difference is that I tried to express that I expect the signal :foo never to be sent.

1

There are 1 answers

3
engineersmnky On BEST ANSWER

Okay so there are 2 problems here. First is you are trying to use rspec expectations syntax with mocha as your mocking frame work. This causes the:

undefined method `receive' for #<RSpec::ExampleGroups::Something:0x0000000010391588>

Error because mocha does not know about receive. The syntax for mocha should be:

it { expects(:foo).never }  

Your next issue is the before block is stubbing foo so it never actually reaches the receiver. So your second case passes because subject never actually receives foo because the stubbing intercepts the call.

Example:

require 'rspec'
require 'mocha'

RSpec.configure do |config|
  config.mock_with :mocha
end

def stub_obj(obj,method_list)
  Class.new(SimpleDelegator) do 
      method_list.each do |k,v| 
          define_method(k) {v}
      end
  end.new(obj)
end

RSpec.describe 'something' do

  subject {mock('subject')}
  #passes 
  it { expects(:foo).never }
  # fails 
  it { subject.expects(:foo).never; subject.foo }
  # passes 
  it 'can hide from foo' do 
    subject.stubs(:foo)
    subject.expects(:foo).never
    subject.foo
  end

  context 'kind of like this' do
    # passes
    it 'hides with a stubbed method' do
      stubber = stub_obj(subject, foo: 'bar')
      subject.expects(:foo).never
      stubber.foo
    end
    # fails 
    it 'cannot hide from a non-stubbed method' do
      stubber = stub_obj(subject, foo: 'bar')
      subject.expects(:bar).never
      stubber.bar
    end
  end
end

Here is the implementation of Object#stubs if you are interested and while it does a bit more than my example (#stub_obj) through a Mocha::Mockery object the concept is the same.