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()
实现。
但是,你不需要为这个例子做任何这些——调用真正的方法是间谍的默认行为。
我用 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()
实现。
但是,你不需要为这个例子做任何这些——调用真正的方法是间谍的默认行为。