mockito-scala
mockito-scala copied to clipboard
Mockito won't stub method as expected if the method implemented with Singleton pattern.
I have a java class (but I think java shouldn't matter, the issue should persist even given a scala implementation)
abstract class MyClass {
String fieldToCacheSerialization = null;
public String serialize() {
if(fieldToCacheSerialization == null) {
fieldToCacheSerialization = expensiveSerialization();
}
return fieldToCacheSerialization;
}
/** delegate to sub class implementation, but it is expensive operation
* and therefore we want to cache it.
* /
public abstract String expensiveSerialization() {...}
}
I want to stub MyClass.serialize()
call in my scala test suite via Mockito:
test("some test should work as expected") {
val myClassMock = mock[MyClass]
when(myClassMock.serialize()).thenReturn("stubbedSerialization") // <--- This will not work as expected.
}
I found the stubbing will not work, as Mockito will register a method stub for MyClass.expensiveSerialization()
instead of MyClass.serialize()
-- however, during the actual method invocation when Mockito registering the stub, fieldToCacheSerialization
will initialize with an empty String (I guess that is the smartNull response for a non-stub yet expensiveSerialization()
invocation?). This will lead the stub register on MyClass.expensiveSerialization()
NEVER served stub call in test!
I currently have to workaround with:
test("some test should work as expected") {
val myClassMock = mock[MyClass]
when(myClassMock.expensiveSerialization()).thenReturn("stubbedSerialization") // <-- Add this line fix the issue
when(myClassMock.serialize()).thenReturn("stubbedSerialization")
However, I kind of dislike the workaround as:
- It leaks
MyClass
implementation details to my test suite -- in my test suite,MyClass.serialize()
is the contact point for my test scope - I do not want to set up the stub only with having to know howMyClass.serialize()
is implemented - It is tricky that stubbing only on
myClassMock.serialize()
won't work -- took me a lot of time to realize is Mockito didn't stub as expected (thought my implementation was wrong and mislead me to debug my implementations).
Wondering is there a way to tell Mockito just directly stub the method invocation that user specified instead of trying to "infer" a leaf method invocation to stub with? (e.g. do not register stub on MyClass.expensiveSerialization()
when user is actually specify the stub on MyClass.serialize()
).
Thanks a lot!