Scala Mockito Guice and Partial Mocking .... methods are called twice
I have the following code written in Scala, Guice, Mockito and 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]
}
}
and I wrote unit test cases with partial mockery
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)
}
}
My tests are successful and I can see that it mocks the correct set of methods and calls the actual implementation of the correct set of methods. BUT when I look at the output I see
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.
Why do I see the print operator came inside
twice?
Edit ::
Based on Tavian's recommendation ... this is the last code that worked
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)
}
}
source to share
For spies, you need to be careful with expressions like
when(x.workHorse()).thenCallRealMethod()
because it is x.workHorse()
actually called in this expression! x
does not "know" that it is inside the call when()
, since the expression is omitted like this:
tmp1 = x.workHorse();
tmp2 = when(tmp1);
tmp3 = tmp2.thenCallRealMethod();
Instead, you can write
doCallRealMethod().when(x).workHorse()
which will suppress the call to the real implementation workHorse()
.
But , you don't need to do any of this in this example: - Calling real methods - default spy behavior.
source to share
As you mentioned in the title of your question, you have a combination of three technologies. There are actually 4 technologies including a build tool that is used to run the test. You can isolate the problem with
1) Remove Guice and build everything directly
2) Run the code as a simple application instead of running as a test with sbt / gradle / maven.
It also makes sense to print the stack trace along with the message came inside
to find the caller.
source to share