Assume I have the following trait, with a single method that receives a call-by-name parameter:
trait Client {
def compute(value: => String): String
}
Also, assume I have the following function:
final def getValue: String = {
"value"
}
Now let's say I'm trying to Mock this class using Mockito (org.specs2.mock.Mockito), the following way:
val client: Client = mock[Client]
client.compute(getValue) returns "result"
The problem is that when the mocked method is invoked, it doesn't return the expected value:
client.compute(getValue) mustEqual "result" // fails. returns null
As you can see, I'm using this parameter actually as a function I send to the method (a bit like a Supplier). I don't understand why the Mocking doesn't work. I cannot write my unit tests as long as I cannot control what client.compute(..) returns.
Help is much appreciated.
Call-by-name parameters are actually compiled into something like this:
trait Client {
def compute(valueFunction => Function0[String]): String
}
and the call is converted into something like this
client.compute(() => { getValue() })
or putting it more explicitly:
client.compute(new Funciton0[String]{ def apply():String = { getValue() }})
So Mockito returns
doesn't work because in two calls of the compute
the parameter being passed is actually two different Function0
objects. And because equals
is not overridden for Function0
, they don't match.
The trick to work this around is to first explicitly convert your getValue
method into a local Function0[String]
variable. This seems to be enough to make two client.compute
calls generate identical objects for Mockito to work. So you may try to use following code:
import org.specs2._
import org.specs2.mock.Mockito
class Specs2Test extends Specification with Mockito {
override def is =
s2"""
this works $good
this doesn't $bad
"""
final def getValue: String = {
"value"
}
def good = {
val client: Client = mock[Client]
val f: Function0[String] = getValue _
client.compute(f()) returns "result"
client.compute(f()) mustEqual "result"
}
def bad = {
val client: Client = mock[Client]
client.compute(getValue) returns "result"
client.compute(getValue) mustEqual "result" // fails. returns null
}
}
Update
If what you actually test is not client.compute
but some other method in Java that inside calls client.compute
and you want to mock that call, I don't know how to help you preserving exact semantics without rewriting at least some of your code. Probably the simplest thing I can think of is to use Funciton0
in the signature explicitly and then use Matchers.any
such as
trait Client {
def compute(value: () => String): String
}
and then
def usingMatchAny = {
val client: Client = mock[Client]
client.compute(ArgumentMatchers.any()) returns "result"
// actually here you call the method that uses client.compute call
client.compute(getValue _) mustEqual "result"
}