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.