2010-01-28 45 views
19

我试图嘲弄一个方法调用,需要一个呼叫按姓名参数:如何在Scala中模拟具有函数参数的方法?

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) 
     } 
    } 
} 

我在主要的Mockito感兴趣,因为这就是我使用的是什么,但我有兴趣看看任何主要的模拟框架是否有能力进行这种测试。测试失败在上verify线运行时,用这样的错误

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) 

如果我理解正确的情况下,编译器隐含在返回t一个无参函数包装t。模拟框架然后将该功能与测试代码中生成的功能进行比较,该功能等效但不是equals()

我的情况是一个相对简单的问题,但我认为这是任何高阶函数的问题。

+3

我不明白这与Apple iPad的关系。 – 2010-01-28 05:58:55

+0

您能否描述它如何更清晰地工作或包含一些测试代码?只需显示验证,就很难知道这里发生了什么。 – 2010-01-28 14:49:35

+0

我将这个示例按照书面形式运行,并包含与失败相关的实际输出。 – 2010-01-28 20:56:13

回答

9

这看起来丑陋,但希望它可以帮助你找到很好的解决方案:

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() 
     }) 
} 
1

这个问题似乎是具体的,因为在常规的高阶函数可以匹配对明确FunctionX按姓名调用对象:

验证(合作者).somethingElse(任何(函数2 [字符串,事]))

在按姓名案的说法包装成Function0是隐式进行

,和阿列克谢的回答显示了如何调用模拟一个明确的参数。

你可以写一些类似于你自己的验证,它会应用mockito捕获的参数。

内部的Mockito记录的调用以及它们与例如为: http://code.google.com/p/mockito/source/browse/trunk/src/org/mockito/internal/matchers/CapturingMatcher.java

+0

这不起作用,因为函数需要Thing(:=> Thing)类型的单个call-by-name参数,它与Function2 [String,Thing](:String => Thing)不是同一类型。为了使用匹配器,需要使用更高级别的类型来输入按名称参数。 – Woodz 2013-06-11 18:18:13

0

参数你可以试试specs2。在规格2中,我们“劫持”了Mockito Invocation类以考虑姓名参数:

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

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

嗨,Eric,我将该片段复制到新的Play 2.2.3应用规范中,但仍然失败。我正在使用一个简单的'mutable.Specification与Mockito'测试类,'specs2_2.10-2.1.1'和'mockito-core-1.9.5'(在'build.sbt'中明确声明,确保specs2被声明之前mockito)任何线索为什么它仍然失败? – jmelanson 2014-09-02 23:49:41

+0

您需要确保specs2 jar在类路径中的Mockito jar之前。 – Eric 2014-09-03 12:38:30

+0

任何提示如何与SBT?我已经尝试更改宣言单。 – jmelanson 2014-09-03 17:23:31

相关问题