13

我正在使用 Scala 2.10、specs2 和 Mockito。我想模拟 scala.io.Source.fromURL()。问题似乎是 fromURL() is a function in io.Source's object

val m = mock[io.Source]
m.fromURL returns io.Source.fromString("Some random string.")

这是单元测试中的一个非常简单的模拟。为什么它不起作用?

谢谢!

4

3 回答 3

24

而不是嘲笑它,你可以尝试spying如下:

val m = spy(io.Source)

或者你可以模拟如下:

val m = mock[io.Source.type]

但是,您如何Source在您正在测试的课程中使用?如果您有这样的示例类:

class MyClass{

  def foo = {
    io.Source.doSomething //I know doSomething is not on Source, call not important
  }
}

然后为了利用模拟/间谍,你必须像这样构造你的类:

class MyClass{
  val source = io.Source
  def foo = {
    source.doSomething
  }
}

然后你的测试必须看起来像这样:

val mockSource = mock[io.Source.type]
val toTest = new MyClass{
  override val source = mockSource
}

在 Java 世界中,静态方法是 mocking 的祸根。在 Scala 世界中,对对象的调用对于单元测试来说也很麻烦。但是如果你遵循上面的代码,你应该能够在你的类中正确地模拟出基于对象的依赖项。

于 2013-05-08T17:24:38.480 回答
4

好消息!使用最新的 1.16 版本mockito-scala,您现在可以模拟 scala object

要启用 withObjectMocked 功能,必须创建包含一行的文件 src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker:

mock-maker-inline 

例子:

object FooObject {   
  def simpleMethod: String = "not mocked!"
}

"mock" should {
  "stub an object method" in {
    FooObject.simpleMethod shouldBe "not mocked!"

    withObjectMocked[FooObject.type] {
      FooObject.simpleMethod returns "mocked!"
      //or
      when(FooObject.simpleMethod) thenReturn "mocked!"

      FooObject.simpleMethod shouldBe "mocked!"
    }

    FooObject.simpleMethod shouldBe "not mocked!"
  } 
}

参见:https ://github.com/mockito/mockito-scala#mocking-scala-object

于 2020-11-09T05:43:26.017 回答
1

多年后,上述答案并不像其他人指出的那样起作用。

而且你不能模拟scala.io.Source对象。

我可以模拟最终/私有方法或类吗?这是不支持的,因为使用宏生成的模拟被实现为模拟类型的子类。所以私有和最终方法不能被覆盖。您可能想尝试在代码中使用适配器或外观以使其可测试。最好针对特征/接口而不是具体实现进行测试。有一些库支持这种模拟,例如 PowerMock。请注意,这种模拟涉及字节码操作,这有可能使您的测试双重偏离实际实现。

所以我所做的是一种解决方法,将其抽象scala.io.Source.fromUrl()为函数参数并在测试中传入模拟函数。

  // original func
  def aFuncThatUsesSource() = {
    val source = scala.io.Source("127.0.0.1:8080/...")
    val result = source.mkString
    Try(source.close())
    result
  }

  // test friendly func that accepts `scala.io.Source.fromURL` as arg
  def aTestFriendlyFunc(makeApiCall: String => BufferedSource) = {
    val source = makeApiCall("127.0.0.1:8080/...")
    val result = source.mkString
    Try(source.close())
    result
  }

....

  // test spec
  def testyMcTesterson = () => {
    val makeApiCall = mockFunction[String, BufferedSource]
    makeApiCall.expects("something...")
      .returns( new BufferedSource(new ByteArrayInputStream("returns something".getBytes)) )
    aTestFriendlyFunc(makeApiCall) shouldEqual "returns something"
  }
于 2020-07-08T18:16:59.620 回答