如何在 Scala 中模拟带有函数参数的方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2152019/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to mock a method with functional arguments in Scala?
提问by Aaron Novstrup
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 verifyline, with an error like
我主要对 Mockito 感兴趣,因为我正在使用它,但我很想知道是否有任何主要的模拟框架能够进行这种测试。测试在运行时失败就verify行了,出现类似的错误
Argument(s) are different! Wanted: collaborator.doSomething( ($anonfun$apply) <function> ); -> at Test$$anonfun$$anonfun$apply.apply(Test.scala:27) Actual invocation has different arguments: collaborator.doSomething( ($anonfun$apply) <function> ); -> at Test$$anonfun$$anonfun$apply.apply(Test.scala:24)
Argument(s) are different! Wanted: collaborator.doSomething( ($anonfun$apply) <function> ); -> at Test$$anonfun$$anonfun$apply.apply(Test.scala:27) Actual invocation has different arguments: collaborator.doSomething( ($anonfun$apply) <function> ); -> at Test$$anonfun$$anonfun$apply.apply(Test.scala:24)
If I'm understanding the situation correctly, the compiler is implicitly wrapping tin 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().
如果我正确理解了这种情况,编译器会隐式地包装t在一个返回t. 然后模拟框架将该函数与测试代码中生成的函数进行比较,后者等效但不是equals().
My case is a relatively simple version of the problem, but I think this would be an issue with any higher-order function.
我的情况是该问题的一个相对简单的版本,但我认为这将是任何高阶函数的问题。
采纳答案by Alexey
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()
})
}
回答by miaubiz
This problem seems to be specific to by-name invocations because in regular higher order functions you can match against the explicit FunctionX object:
这个问题似乎特定于按名称调用,因为在常规的高阶函数中,您可以匹配显式 FunctionX 对象:
verify(collaborator).somethingElse(any(Function2[String, Thing]))
验证(合作者).somethingElse(任何(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.
在按名称的情况下,将参数包装到 Function0 中是隐式完成的,Alexey 的回答显示了如何使用显式参数调用模拟。
You could write something akin to your own verify which would apply arguments captured by mockito.
你可以写一些类似于你自己的验证的东西,它会应用由 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
Mockito 在内部记录调用及其参数,例如:http: //code.google.com/p/mockito/source/browse/trunk/src/org/mockito/internal/matchers/CapturingMatcher.java

