RSpec::Mocks::VerifyingMessageException does not implement method

Why am I trying to call RSpec::Mocks::VerifyingMessageException?

The problem started with an attempt to create a stub like the following:

    new_net=allow(Net::SFTP).to receive(:start).and_return(double('sftp'))
    allow(new_net).to receive(:upload_file!).and_raise('Broken')

Which resulted in the following failure

     Failure/Error: allow(new_net).to receive(:upload_file!).and_raise('Broken')
       #<RSpec::Mocks::VerifyingMessageExpectation Net::SFTP.start(any arguments)> does not implement: upload_file!

It’s a pretty obscure error to look up because RSpec::Mocks::VerifyingMessageException is not a class you would normally explicitly interact with. So what happened?

RSpec::Mocks::VerifyingMessageException is the class of an allow call

new_net=allow(Net::SFTP).to receive(:start).and_return(double('sftp')) isn’t saving off the double, but the result of the allow call. While at a fresher point in the day, I would have noticed that, I was somehow expecting the double itself to be returned. However, usually when an object is returned, it’s part of the method chain, not an argument in a part of the chain.

The proper setup would have been to save the double, set it as a return value, and stub a method on it:

        new_net=double('sftp')
        allow(Net::SFTP).to receive(:start).and_return(new_net)
        allow(new_net).to receive(:upload_file!).and_raise('Broken')

(The above example is a bit contrived for illustration purposes and is also not necessarily the best approach for a given circumstance.)

A related error when using and_wrap_original

If you’re wrapping the original new of a class and then want to add stubs, you can use and_wrap_original, but beware a pitfall (again, contrived example):

        allow(String).to receive(:new).and_wrap_original do |m, *args|
          string = m.call(*args)
          allow(string).to receive(:downcase).and_return('downcase')
        end

Which will generate an error like:

     1.2) Failure/Error: String.new('hi').downcase

          NoMethodError:
            undefined method `downcase' for #<RSpec::Mocks::VerifyingMessageExpectation:0x00007fede75f9380>

Fortunately, this is a simple case of not having the subject object (that needs to be returns by the method) as the last thing evaluated in the block:

        allow(String).to receive(:new).and_wrap_original do |m, *args|
          string = m.call(*args)
          allow(string).to receive(:downcase).and_return('downcase')
          string
        end

Conclusion

I hope that you don’t back yourself into these corners with stubbing methods, but if you manage to get this error, I hope you either find this post or have remembered it for the future.