模拟 scala 对象

18

我正在使用mockito,尝试对一个Scala对象进行模拟。

object Sample { }
//test
class SomeTest extends Specification with ScalaTest with Mockito {
    "mocking should succeed" in {
        val mockedSample = mock[Sample]
     }
}

这给了我两个编译错误。

error: Not found type Sample
error: could not find implicit value for parameter m:
scala.reflect.ClassManifest[<error>]

如果我将Sample对象更改为类,则可以工作。 是否可能使用mockito模拟Scala对象?如果可以,如何实现?

4个回答

18

按照当前写法,你的Sample是一个纯单例。它的类型是唯一的,并且只有一个该类型的成员存在。Scala object可以扩展另一个类(可能是抽象类,如果它提供了必要的定义来使其成为具体类)和traits。这样做会赋予它一个类型身份,其中包括这些祖先。

我不知道Mockito实际上在做什么,但在我看来,你所要求的内容与Scala object的本质相悖。


3
完全同意。你可以尝试使用mock[Sample.type],但我怀疑在实践中是否可行。我建议让Sample扩展一些接口特性并以此方式进行模拟。问题在于,如果你想要注入模拟对象来替代单例Sample,你将需要进行一些有趣又邪恶的反射魔术。如果你有兴趣,我们可以发布相关内容。 - jsuereth
前几天我在查看Mockito源代码以回答一个相关的问题(现在出于某种原因我找不到了),我似乎记得看到其中一个“mock”生成器只返回特定的值,而不是尝试生成替代/变体。 - Randall Schulz
有没有使用Powermock或类似工具的方法可以实现这个?如果我在一个伴生对象上有一个(有效的全局)方法,想要模拟它的行为似乎是合理的;在Ruby世界中,这肯定是一种常见的模式。 - Korny
1
jsuereth,我知道这是一个旧帖子,但我想知道如何将模拟对象注入到单例对象中。谢谢。 - k4200

16

记住,如果将其提升为函数,则您可以模拟对象方法

case class Person(name: String)
object Person {
  def listToJson(lp: List[Person]) = "some actual implementation"
}

class ClassUnderTest(listToJson: (List[Person]) => String = Person.listToJson(_)) {
  def testIt(lp: List[Person]) = listToJson(lp)
}

import org.specs._
import org.specs.mock.Mockito
import org.mockito.Matchers._  

class ASpec extends Specification with Mockito {
  "a thing" should {
    "do whatever" in {
      val m = mock[(List[Person]) => String]
      val subject = new ClassUnderTest(m)
      m(Nil) returns "mocked!"
      subject.testIt(Nil) must_== "mocked! (this will fail on purpose)"
    }
  }
}

我这里并不是嘲讽Person对象本身,而是嘲讽它上面的方法(这大概是提问者原本想表达的意思)。

测试结果显示模拟(mocking)生效了:

[info] == ASpec ==
[error] x a thing should
[error]   x do whatever
[error]     'mocked![]' is not equal to 'mocked![ (this will fail on purpose)]' (ASpec.scala:21)
[info] == ASpec ==

同时,由于注入的函数是默认参数,因此 ClassUnderTest 的生产期使用仅需 new ClassUnderTest


11

自mockito-scala 1.16.0版本以来,可以对Scala object进行模拟,您可以在此处查看文档,但以下是它的示例。

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!"
 }
}

8

我最近发布了ScalaMock,这是一个适用于Scala的模拟库,可以模拟单例(和伴生)对象等内容。


6
在提供的页面上,在标题为“未来计划”的部分声明了对单例对象和伴生对象的支持。目前该框架是否支持这些功能? - zbstof
3
看起来只有使用 2.9 编译器插件的 ScalaMock2 才支持模拟对象,具体解释在这个教程中。ScalaMock3 不需要编译器插件,而是依赖于 Scala 2.10 的宏,但是它还不支持所有的 ScalaMock2 功能,详见这篇博客文章。因此,在 2.10 中不支持模拟对象。据我所知,这仍然是该项目当前的状态。 - Felix GV
4
ScalaMock目前的文档与这个答案直接相矛盾。请参见FAQ中的“Can I mock objects?”部分。 - Noah Solomon
Noah 对我来说似乎是正确的。这个答案现在是否有效/正确? - akki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接