23

I'm trying to mock a method call that takes a call-by-name argument:

import org.scalatest.WordSpec
import org.scalatest.mock.MockitoSugar
import org.mockito.Mockito._
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner

trait Collaborator {
   def doSomething(t: => Thing)
}

trait Thing

@RunWith(classOf[JUnitRunner])
class Test extends WordSpec with MockitoSugar {
   "The subject under test" should {
      "call the collaborator" in {
         // setup
         val m = mock[Collaborator]
         val t = mock[Thing]

         // test code: this would actually be invoked by the SUT
         m.doSomething(t)

         // verify the call
         verify(m).doSomething(t)
      }
   }
}

I'm primarily interested in Mockito since that's what I'm using, but I'd be interested to see whether any of the major mock frameworks is capable of this kind of testing. The Test fails at runtime on the verify line, with an error like

Argument(s) are different! Wanted:  
collaborator.doSomething(  
   ($anonfun$apply$3) <function>  
);  
-> at Test$$anonfun$1$$anonfun$apply$1.apply(Test.scala:27)  
Actual invocation has different arguments:  
collaborator.doSomething(  
    ($anonfun$apply$2) <function>  
);  
-> at Test$$anonfun$1$$anonfun$apply$1.apply(Test.scala:24)  

If I'm understanding the situation correctly, the compiler is implicitly wrapping t in a nullary function that returns t. The mock framework is then comparing that function to the one produced in the test code, which is equivalent but not equals().

My case is a relatively simple version of the problem, but I think this would be an issue with any higher-order function.

3
  • 3
    I don't understand how this relates to the Apple iPad. Commented Jan 28, 2010 at 5:58
  • Could you describe how it doesn't work more clearly or include some test code. It's hard to know what's going on here with just the verify shown. Commented Jan 28, 2010 at 14:49
  • I made the example runnable as written and included the actual output associated with the failure. Commented Jan 28, 2010 at 20:56

3 Answers 3

11

This looks ugly, but hopefully it can help you to find good solution:

import org.scalatest.mock.MockitoSugar
import org.mockito.Mockito._

trait Collaborator {
   def doSomething(t: => Thing)
}

trait Thing

new MockitoSugar {
     // setup
     val m = mock[Collaborator]
     val t = mock[Thing]

     m.doSomething(t)

     classOf[Collaborator].getMethod("doSomething", classOf[Function0[_]]).invoke(
        verify(m), 
        new Function0[Thing] { 
            def apply() = null
            override def equals(o: Any): Boolean = t == o.asInstanceOf[Function0[Thing]].apply() 
      })
}
Sign up to request clarification or add additional context in comments.

Comments

2

You can try specs2. In specs2, we "hijack" the Mockito Invocation class to account for byname parameters:

trait ByName { def call(i: =>Int) = i }
val byname = mock[ByName]

byname.call(10)
there was one(byname).call(10)

4 Comments

Hi Eric, I copied that snippet into a new Play 2.2.3 app spec and it still fails. I'm using a simple mutable.Specification with Mockito test class, specs2_2.10-2.1.1 and mockito-core-1.9.5 (declared explicitly in build.sbt, making sure specs2 is declared before mockito) Any clue why it still fails?
You need to make sure that the specs2 jar comes before the Mockito jar on the classpath.
Any hint how to that with SBT? I already tried changing declaration order.
That's how I'm doing and it usually works. One thing you can try is to depend on specs2-core and specs2-mock without depending on mockito because specs2-mock will drag it for you and hopefully the order will be right.
1

This problem seems to be specific to by-name invocations because in regular higher order functions you can match against the explicit FunctionX object:

verify(collaborator).somethingElse(any(Function2[String, Thing]))

in the by-name case the wrapping of the argument into a Function0 is done implicitly, and Alexey's answer shows how to invoke the mock with an explicit parameter.

You could write something akin to your own verify which would apply arguments captured by mockito.

Mockito internally records invocation and their arguments with e.g.: http://code.google.com/p/mockito/source/browse/trunk/src/org/mockito/internal/matchers/CapturingMatcher.java

1 Comment

This does not work, as the function expects a single call-by-name parameter of type Thing (: => Thing), which is not the same type as Function2[String, Thing] (:String => Thing). There needs to be a higher-kinded type for typing call-by-name parameters in order to use matchers.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.