Scala Mockito Guice 和 Partial Mocking ....方法被调用两次

Scala Mockito Guice and Partial Mocking .... methods get called twice

我用 Scala、Guice、Mockito 和 ScalaTest 编写了以下代码

import javax.inject.Singleton
import com.google.inject.Inject
@Singleton
class TestPartialMock @Inject()(t1: Test1, t2: Test2) {
   def test3() = "I do test3"
   def workHorse() : List[String] = {
      println("+++++ came inside ++++++++")
      List(t1.test1(), t2.test2(), test3())
   }
}


class MainModule extends ScalaModule {
   override def configure() = {
      bind[Test1]
      bind[Test2]
      bind[TestPartialMock]
   }
}

并且我编写了带有部分模拟的单元测试用例

class PartialMockTest extends FunSpec with Matchers {
   describe("we are testing workhorse but mock test3") {
      it("should return mock for test3") {
         val module = new TestModule
         val injector = Guice.createInjector(module)
         val tpm = injector.getInstance(classOf[TestPartialMock])
         val result = tpm.workHorse()
         result should contain ("i do test2")
         result should contain ("i do test1")
         result should contain ("I am mocked")
         result should not contain ("I do test3")
      }
   }
}

class TestModule extends AbstractModule with ScalaModule with MockitoSugar {
   override def configure() = {
      val module = new MainModule()
      val injector = Guice.createInjector(module)
      val realobject = injector.getInstance(classOf[TestPartialMock])
      val x = spy(realobject)
      when(x.test3()).thenReturn("I am mocked")
      when(x.workHorse()).thenCallRealMethod()
      bind(classOf[TestPartialMock]).toInstance(x)
   }
}

我的测试成功了,我可以看到它模拟了正确的方法集并调用了正确方法集的实际实现。但是当我查看输出时,我看到

info] Compiling 5 Scala sources to /Users/IdeaProjects/GuicePartialMock/target/scala-2.12/classes...
[info] Compiling 1 Scala source to /Users/IdeaProjects/GuicePartialMock/target/scala-2.12/test-classes...
+++++ came inside ++++++++
+++++ came inside ++++++++
[info] PartialMockTest:
[info] we are testing workhorse but mock test3
[info] - should return mock for test3
[info] Run completed in 2 seconds, 92 milliseconds.

为什么我看到打印语句 came inside 两次?

编辑::

根据 Tavian 的建议...这是最终有效的代码

class TestModule extends AbstractModule with ScalaModule with MockitoSugar {
   override def configure() = {
      val module = new MainModule()
      val injector = Guice.createInjector(module)
      val realobject = injector.getInstance(classOf[TestPartialMock])
      val x = spy(realobject)
      when(x.test3()).thenReturn("I am mocked")
      bind(classOf[TestPartialMock]).toInstance(x)
   }
}

正如您在问题标题中提到的,您结合了 3 种技术。实际上有 4 项技术,包括用于 运行 测试的构建工具。您可以通过

来隔离问题

1) 去掉Guice,直接实例化一切

2) 运行 代码作为一个简单的应用程序,而不是 运行ning 作为 sbt/gradle/maven 的测试。

此外,将堆栈跟踪与 came inside 消息一起打印以查找调用者也很有意义。

间谍需要注意

这样的表达
when(x.workHorse()).thenCallRealMethod()

因为x.workHorse()真的在这个表达式中被调用了! x 不 "know" 它在 when() 调用中,因为表达式被降低为如下所示:

tmp1 = x.workHorse();
tmp2 = when(tmp1);
tmp3 = tmp2.thenCallRealMethod();

相反,您可以写

doCallRealMethod().when(x).workHorse()

这将抑制调用真正的 workHorse() 实现。

但是,你不需要为这个例子做任何这些——调用真正的方法是间谍的默认行为。