如何使用视图边界作为参数来模拟函数

时间:2017-04-24 04:12:12

标签: scala mockito scalatest specs2 scalamock

我在Scala中有一个函数,如下所示

object MyService extends MyTrait {    
  def myMethod[T <% InvokableBuilder[MyClass]](builder: T): MyResponse = {
  //do something
  }
}

我正在尝试使用mockito来模拟此功能,如下所示

val mockService = mock[MyTrait]

doReturn(info).when(mockService).myMethod(any())

虽然函数

中只有一个参数,但我收到以下错误
  

org.mockito.exceptions.misusing.InvalidUseOfMatchersException:参数匹配器的使用无效!   [info] 2匹配预期,1记录:

2 个答案:

答案 0 :(得分:1)

你的问题是:scala 不是 java。

你的隐含假设是:&#34;那里的小scala方法被翻译成类似于Java的东西;因此我可以简单地使用mockito来处理那个&#34;。

错误。您正在此处创建scala 对象定义;如果我没记错的话; scala中的 object ...转换为Java中的 static (例如,请参阅here)。

因此,乍一看,您可能需要转向PowerMock(ito)。 JMockit是为了模拟那些静态元素。 (以及我通常的警告:不要使用PowerMock;因为嘲弄静态内容是糟糕的想法)。正如Philipp M在他的评论中指出的那样:嘲笑静态真的被认为是不好的做法。你应该考虑嘲笑&#34; trait&#34;事情的一面。

所以真正的答案是:你必须知道自己在做什么。 Mockito是为 java 编写的。你不能只假设你在scala中记下的任何东西,并且&#34;看起来不知何故&#34;像Java一样可以很容易地映射到Mockito正在开展工作的概念。

为了真正了解正在发生的事情;您应首先查看scala编译器在您的案例中创建的类文件;查看方法签名;并为自己思考:&#34;如果我必须在java源代码中调用该方法,我该怎么做?&#34;并从那里开始工作。

答案 1 :(得分:0)

我改变了你的//做了什么?所以它会编译,然后在解析器阶段结束时打印代码:

$scalac MyService.scala -Xprint:parser
[[syntax trees at end of                    parser]] // MyService.scala
package <empty> {
  object MyService extends MyTrait {
    def <init>() = {
      super.<init>();
      ()
    };
    def myMethod[T](builder: T)(implicit evidence$1:_root_.scala.Function1[T,InvokableBuilder[MyClass]]): MyResponse = $qmark$qmark$qmark
  }
}

如您所见,由于您的视图绑定,myMethod有第二个参数列表。 我不确定你会如何用Mockito来嘲笑它,但我建议你试试ScalaMock。

注意:不推荐使用视图边界 - 我建议用隐式参数替换它们(请参阅scalac解析器如何执行上述操作)。

龙的例子:

import org.scalamock.scalatest.MockFactory
import org.scalatest.FlatSpec

import scala.language.implicitConversions

class FooTest extends FlatSpec with MockFactory {

  trait MyTrait {
    def myMethod[T](builder: T)(implicit ev$1: T => InvokableBuilder[MyClass]): MyResponse
  }

  trait InvokableBuilder[T]

  class MyClass

  class MyResponse

  class Foo

  object MyService extends MyTrait {
    def myMethod[T](builder: T)(implicit ev$1: T => InvokableBuilder[MyClass]): MyResponse = {
      //do something
      ???
    }
  }

  behavior of "Foo"

  it should "foo" in {
    val x = mock[MyTrait]

    implicit val fooConvert: Foo => InvokableBuilder[MyClass] = ???
    (x.myMethod(_: Foo)).expects(*).once()
  }

}