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
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
- Nobody is watching this ticket.