【问题标题】:Scala Mockito Guice and Partial Mocking .... methods get called twiceScala Mockito Guice 和 Partial Mocking .... 方法被调用两次
【发布时间】:2017-05-04 05:08:43
【问题描述】:

我有以下用 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)
   }
}

【问题讨论】:

  • 对于 Mockito 间谍,when(x.workHorse()) 已经调用了真正的 workHorse() 方法。事实上,不需要.thenCallRealMethod(),因为这是间谍的默认行为。

标签: scala mockito guice scalatest


【解决方案1】:

对于间谍,你需要小心类似的表达

when(x.workHorse()).thenCallRealMethod()

因为x.workHorse() 真的在这个表达式中被调用了! x 不“知道”它在 when() 调用中,因为表达式被简化为如下内容:

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

相反,你可以写

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

这将禁止调用真正的workHorse() 实现。

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

【讨论】:

  • 我尝试了你的建议。我用新代码编辑了上面的帖子。但我仍然看到两个调用。
  • 但是你没有采纳我的建议 (doCallRealMethod().when(x).workHorse())
【解决方案2】:

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

来隔离问题

1) 移除 Guice 并直接实例化所有内容

2) 将代码作为简单的 App 运行,而不是通过 sbt/gradle/maven 作为测试运行。

同时打印堆栈跟踪和came inside 消息以查找调用者也是有意义的。

【讨论】:

  • 所有适用于 Guice 的东西都被删除了。整篇文章的重点是我想和 Guice 一起做。这就是问题的症结所在。否则很简单。
猜你喜欢
  • 2021-11-26
  • 2021-11-04
  • 1970-01-01
  • 2013-12-31
  • 1970-01-01
  • 2020-07-16
  • 2014-09-02
  • 1970-01-01
  • 2014-05-22
相关资源
最近更新 更多