假设我有以下特征,一个方法接收一个按名称调用的参数:
trait Client {
def compute(value: => String): String
}
另外,假设我有以下功能:
final def getValue: String = {
"value"
}
现在让我说我正在尝试使用Mockito(org.specs2.mock.Mockito)模拟这个类,方法如下:
val client: Client = mock[Client]
client.compute(getValue) returns "result"
问题是,当调用mocked方法时,它不会返回预期值:
client.compute(getValue) mustEqual "result" // fails. returns null
正如您所看到的,我实际上将此参数用作我发送给方法的函数(有点像供应商)。我不明白为什么Mocking不起作用。只要我无法控制client.compute(..)返回的内容,我就无法编写单元测试。
非常感谢帮助。
答案 0 :(得分:2)
按名称调用参数实际上编译成如下:
trait Client {
def compute(valueFunction => Function0[String]): String
}
并将调用转换为类似
的内容client.compute(() => { getValue() })
或更明确地说明:
client.compute(new Funciton0[String]{ def apply():String = { getValue() }})
所以Mockito returns
不起作用,因为在compute
的两次调用中,传递的参数实际上是两个不同的Function0
个对象。由于equals
没有覆盖Function0
,因此它们不匹配。
解决这个问题的方法是首先将getValue
方法明确转换为本地Function0[String]
变量。这似乎足以让两个client.compute
调用为Mockito生成相同的对象。因此,您可以尝试使用以下代码:
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
}
}
<强>更新强>
如果您实际测试的内容不是client.compute
,而是Java内部调用client.compute
并且您想要模拟该调用的其他方法,我不知道如何帮助您保留精确的语义至少重写一些代码。我能想到的最简单的事情可能是在签名中明确使用Funciton0
,然后使用Matchers.any
,例如
trait Client {
def compute(value: () => String): String
}
然后
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"
}
答案 1 :(得分:0)
模拟的问题与以下事实有关:您没有明确声明mp
特征中compute
方法的返回类型,并且假设没有正文,则推断成为Client
。
因此,当您尝试使用
定义模拟行为时Unit
您没有定义该方法的行为。 您必须显式声明公共方法的返回类型