ruby-on-railsmocha.jstestunit

Mocha: stubbing method with specific parameter but not for other parameters


I want to stub a method with Mocha only when a specific parameter value is given and call the original method when any other value is given.

When I do it like this:

MyClass.any_instance.stubs(:show?).with(:wanne_show).returns(true)

I get an

unexpected invocation for MyClass.show?(:other_value)

I also know, that I can stub all parameters when writing the mock without the ´with´-call and then give my specific mock. But then I have to know the return value for every call, which is not the case :/

tldr; Is there a way to call the original method in a stub or to stub just specific parameters and leave the others?


Solution

  • 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.