I spent an hour today trying and failing to get Mocha to allow me to only stub a particular session variable, the way that Rspec allows with ease. While I couldn't figure out a way to make this work, I did figure out a hackish workaround that might help some people, depending on the circumstances.
My "solution" was to remove the session stub after the target instance variable had been grabbed:
ActionDispatch::Request::Session.any_instance.stubs(:delete).returns(state).then.returns(nonce).then.with do |sym|
ActionDispatch::Request::Session.any_instance.unstub(:delete) if sym == :login_nonce
true
end
The trick I'm using here is, by knowing the arguments that will be passed to session.delete
in the first two calls made for a particular action, I can remove the stub after that second delete
call (for login_nonce
) has been made, so the session begins behaving like normal again.
Another potentially useful aspect of constructing a with
block like this is that the block has the full context of the caller, so one can directly inspect or extract session contents within the block. That is, if you wanted a test to grab the value of the blah
session key, you should be able to write something like
ActionDispatch::Request::Session.any_instance.stubs(:[]).with do |key|
@blah = session[key] if key == :blah
true
end
As best I can tell, the with
block always has to return true, otherwise Mocha will throw an Minitest::Assertion: unexpected invocation
exception, because it doesn't know what to do if it has stubbed a method but the argument passed in doesn't match an argument that it can handle. The fundamental problem seems to be that once one calls stubs
on any_instance
, you can no longer have Mocha return a value from the actual session
instance (unlike Rspec, which allows falling back to the original object using and_call_original
as in the linked answer above).
Hopefully someone can use build upon some of these ideas to fashion a more elegant answer in the future, but since almost 8 years have passed and there are no answers, I figure this might be a usable starting point.