如何在Scala中使用函数参数模拟方法?

时间:2010-01-28 03:05:48

标签: scala mocking

我正在尝试模拟一个带有call-by-name参数的方法调用:

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的nullary函数中。然后模拟框架将该函数与测试代码中生成的函数进行比较,该函数是等效的但不是equals()

我的案例是一个相对简单的问题版本,但我认为这对于任何高阶函数都是一个问题。

3 个答案:

答案 0 :(得分: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 :(得分:1)

此问题似乎特定于名称调用,因为在常规高阶函数中,您可以匹配显式的FunctionX对象:

验证(协作者).somethingElse(any(Function2 [String,Thing]))

在by-name的情况下,将参数包装到Function0中是隐式完成的,而Alexey的答案显示了如何使用显式参数调用mock。

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

Mockito在内部记录调用及其参数,例如: http://code.google.com/p/mockito/source/browse/trunk/src/org/mockito/internal/matchers/CapturingMatcher.java

答案 2 :(得分:0)

您可以尝试specs2。在specs2中,我们“劫持”Mockito Invocation类来解释byname参数:

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

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