I've asked this questions also in: https://stackoverflow.com/questions/38766278/mockito-in-specs2-evaluates-a-by-name-functions-when-using-spy.
I'm trying to understand the behaviour of the 'spy' function in 'Mockito' (specs2), when I'm trying to run the following test code:
import org.specs2.mock.Mockito
import org.specs2.mutable.SpecificationWithJUnit
import org.specs2.specification.Scope
import scala.util.{Failure, Success, Try}
class Foo {
def foo(f: => Int): Unit = Try { f } match {
case Success(value) => value
case Failure(_) => foo1()
}
def foo1(): Unit = ???
}
class Boo {
def boo(): Int = ???
}
class SomeTestClass extends SpecificationWithJUnit {
"something" should {
"happen" in new Context {
mockedBoo.boo returns 1
spiedFoo.foo(mockedBoo.boo())
there was one(spiedFoo).foo1
}
}
}
trait Context extends Scope with Mockito {
val spiedFoo = spy(new Foo)
val mockedBoo = mock[Boo]
}
It seems that when calling:
spiedFoo.foo(mockedBoo.boo())
Mocked boo is evaluated before 'Foo.foo' gets the chance to run it's code. I added a breakpoint inside 'boo' and checked the stacktrace which told me that it was invoked by 'MethodInterceptorFilter'. More specifically by 'ArgumentsProcessor.argumentsToMatchers' which looks like this:
if (!isCallRealMethod) {
// evaluate the byname parameter to collect the argument matchers
// if an exception is thrown we keep the value to compare it with the actual one (see "with Any" in the MockitoSpec and issue 82)
val value = try {
arg.asInstanceOf[Function0[_]].apply()
} catch {
case e: Throwable => e
}
// during the evaluation of the value some matchers might have been created. pull them.
val argumentsMatchers = ThreadSafeMockingProgress2.pullLocalizedMatchers
// if there are no matchers at all being registered this means that
// we are invoking the method, not verifying it
// in that case add a new matcher corresponding to the argument (an equals matcher using the value)
if (argumentsMatchers.isEmpty) matchers.add(new EqualsFunction0(value))
else {
// otherwise we add all the existing arguments matchers +
// we reset the state of the argumentMatchersStorage
matchers.addAll(argumentsMatchers.map(_.getActualMatcher))
ThreadSafeMockingProgress2.reportMatchers(argumentsMatchers.map(_.getActualMatcher))
}
}
It clearly tells us: "evaluate the byname parameter to collect the argument matchers". How do I avoid the evaluation of the by-name parameter?
Thanks!