mockito 在另一个对象下模拟由调用的scala对象

o2rvlv0m  于 2022-11-08  发布在  Scala
关注(0)|答案(1)|浏览(124)

我正在尝试为object 1下的一个函数编写单元测试。

object Object1 {
  def main(sysArgs: Array[String]): Unit = {
     val inputDF: DataFrame = UtilObject.getInput()
  }
}

object UtilObject {
  def getInput(){
   ...
  }
}

为了编写单元测试,我使用了MockitoSugar。

"object1Main" should "should make correct calls" in {
    val inputArgs = Array("abc")
    val util = mock[UtilObject.type]

    when(util.getInput().thenReturn(inputData))

    Object1.main(inputArgs)
  }

在运行测试时,它不考虑util mock,只执行getInput()函数。
我想我错过了一些注射。有什么想法吗?
提前感谢!

lsmd5eda

lsmd5eda1#

从概念上讲,模拟Scala对象应该是不可能的。Scala中的对象是纯单例对象。这意味着在任何时候都只能有一个该类型的成员。
mockito-scala可以通过 reflection 模拟Scala对象。我将使用String的结果类型,而不是DataFrame,但想法是相同的:

object UtilObject {
    def getInput(): String = {
      // ...
      "done"
    }
  }

  object Object1 {
    def main(sysArgs: Array[String]): String = {
      val inputDF: String = UtilObject.getInput()
      inputDF
    }
  }

  // in test file:
  "object1Main" should {
    "should make correct calls" in {
      val inputArgs = Array("abc")

      withObjectMocked[UtilObject.type] {
        UtilObject.getInput() returns "mocked!"

        Object1.main(inputArgs) shouldBe "mocked!"
      }

      Object1.main(inputArgs) shouldBe "done"
    }
  }

这只会在withObjectMocked的区块内模仿单一的方法。
通常,这种强大的技术往往会被过度使用或误用,所以我一般不推荐使用它们,除非设计无法重构。
幸运的是,您可以:最简单的方法是对一个类或函数使用依赖注入。2对于带有类的DI,你需要将被模拟的对象转换成一个类:

class UtilObject {
    def getInput(): String = {
      // ...
      "done"
    }
  }

  object Object1 {
    def main(sysArgs: Array[String], ut: UtilObject): String = {
      val inputDF: String = ut.getInput()
      inputDF
    }
  }

  // in test file:
  "object1Main" should {
    "should make correct calls" in {
      val inputArgs = Array("abc")
      val util      = mock[UtilObject]

      when(util.getInput()).thenReturn("mocked!")

      Object1.main(inputArgs, util) shouldBe "mocked!"
    }
  }

对于带有函数的DI,您需要将要模拟的方法提升到函数中:

object UtilObject {
    def getInput(): String = {
      // ...
      "done"
    }
  }

  object Object1 {
    def main(sysArgs: Array[String], f: () => String = UtilObject.getInput): String = {
      val inputDF: String = f()
      inputDF
    }
  }

  // in test file:
  "object1Main" should {
    "should make correct calls" in {
      val inputArgs = Array("abc")
      val f         = mock[() => String]

      when(f()).thenReturn("mocked!")

      Object1.main(inputArgs, f) shouldBe "mocked!"
    }
  }

因为这个函数没有参数,你可以把它转换成一个by-name参数,这个交给你了。
另一种方法是创建一个trait,其中包含你想要模拟的方法,然后用对象来扩展它,但是现在Object1需要是一个类,并且要有一个对被模拟对象的引用:

object UtilObject extends Utils {
    def getInput(): String = {
      // ...
      "done"
    }
  }

  trait Utils {
    def getInput(): String
  }

  class Object1 {
    val uo: Utils = UtilObject
    def main(sysArgs: Array[String]): String = {
      val inputDF: String = uo.getInput()
      inputDF
    }
  }

  // in test file: 
  "object1Main" should {
    "should make correct calls" in {
      val classUnderTest = new Object1 {
        override val uo = mock[Utils]
      }
      val inputArgs = Array("abc")

      when(classUnderTest.uo.getInput()).thenReturn("mocked!")

      classUnderTest.main(inputArgs) shouldBe "mocked!"
    }
  }

相关问题