#47 new
alpinegizmo

Mocking classmethods used by the testing infrastructure can lead to infinite recursion

Reported by alpinegizmo | April 17th, 2009 @ 11:18 AM

Mocking a classmethod, such as Time.now, that is also called by the testing infrastructure (eg within the context of mocha_verify or add_failure), can lead to meltdown. The attached test, abstracted from a large rails application, ends up in an infinite recursion.

Versions of mocha up to 0.9.0 avoided this problem. A change in the logic of Mocha::Mock.method_missing is responsible for the changed behavior. Modifying method_missing to work like this would restore the old behavior:


    def method_missing(symbol, *arguments, &block)
      if @responder and not @responder.respond_to?(symbol)
        raise NoMethodError, "undefined method `#{symbol}' for #{self.mocha_inspect} which responds like #{@responder.mocha_inspect}"
      end
      if matching_expectation_allowing_invocation = @expectations.match_allowing_invocation(symbol, *arguments)
        matching_expectation_allowing_invocation.invoke(&block)
      else
        matching_expectation = @expectations.match(symbol, *arguments)
        
        if matching_expectation
          return matching_expectation.invoke # the old behavior
        elsif !@everything_stubbed
          message = UnexpectedInvocation.new(self, symbol, *arguments).to_s
          message << Mockery.instance.mocha_inspect
          raise ExpectationError.new(message, caller)
        end
      end
    end

This is probably not a good way to resolve this issue, it's just to illustrate what changed. With the old behavior, when a mocked classmethod like Time.now is called by mocha after the test method has completed, the mocked value is returned. The current behavior is that the mocked value is only returned if a new invocation is allowed, otherwise an exception is raised (and caught) and we can end up in an infinite recursion.

I think a cleaner solution lies in the direction of unstubbing everything immediately after the test method has returned, before calling mocha_verify. Similarly each of the rescue clauses (eg for Mocha::ExpectationError) should also unstub before calling add_failure or add_error. This way mocha would then be using the original classmethod, instead of (inappropriately) continuing to use the mocked version after running the test.

No comments found

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile ยป

A mocking & stubbing library for Ruby.

* <a href="http://github.com/floehopper/mocha">GitHub repository</a>
* <a href="http://mocha.rubyforge.org">Documentation</a>
* <a href="http://groups.google.com/group/mocha-developer">Mailing List</a>

People watching this ticket

Attachments

Tags

Pages