Rspec Mocking private method to return error, also rspec Throwing?

Hey guys,

I’m testing an object, and I need a private method on that object to raise an error, that gets rescued by the call stack later in that object.

Upon reading the rspec docs I discovered throw. Whats the difference between .and_throw() & .and_raise()
why would I want to throw a symbol and then catch it later in rspec?

So far I have:

    updater = Updater.new(params
    allow(updater).to receive(:private_method).and_throw(Error)
    updater.run

   expect(updater.have_result_of_rescue).to eq true

So the above is correct. Something else was throwing an error that was throwing me off.

Still not sure what context .add_throw() could be used in

Found this that might help explain it a bit better…

From https://hasno.info/ruby-gotchas-and-caveats/

catch/throw are not the same as raise/rescue. catch/throw allows you to quickly exit blocks back to a point where a catch is defined for a specific symbol, raise rescue is the real exception handling stuff involving the Exception object. If your exception doesn’t inherit from StandardError it will NOT be caught by default!

@ChrisCPO and_throw and and_raise are in RSpec because Ruby provides both throw and raise.

raise / rescue are the error handling mechanism you are probably familiar with. throw / catch, although it behaves similarly to raise / rescue is used for control flow (when it is used at all). You will almost never encounter throw in Ruby. Don’t confuse Ruby’s ‘throw’ statement with ‘raise’ (Example) digs into the differences between the two.

Backing up a bit, testing private methods is considered an anti-pattern. Tests are all about the what and object does rather than the how. An object’s public interface are what it does. The private methods are implementation details. As a user of the object, you don’t need to know that they exist, or wether the public methods call out to private methods, other objects, or have all the logic inside themselves. The how remains nicely hidden within the encapsulation of the object.

Instead of testing private methods, test the behavior indirectly by testing the public methods on your object.

@JoelQ1 thanks for the info, will look into throw.

Yea I don’t test my private methods, this was for testing a pubilc one but I needed to test its response if an error was raised then rescued. The first thing I thought of was mocking a method in the call stack to raise an error, they were all private. Guess I need a better way to do that. Suggestions?

@ChrisCPO If the raising behavior is part of the object being tested, I wouldn’t stub it but would instead re-create the circumstances that cause the public method you are calling to raise the error (from the point of view of the test, it doesn’t matter if the error is actually raised by a private method called by the public method or by the public method directly).

If the raising is being done by a collaborator, then I would stub that collaborator’s public method to raise the expected error.

In general, you should avoid stubbing the object that you are testing (commonly referred to as the “System Under Test” or SUT). It can lead to subtle bugs and false tautologies.

Check out this post on the thoughtbot blog on testing private methods and stubbing the system under test: Don't Stub the System Under Test

1 Like