Description
Consider a Kotlin class with companion object:
class MyClass {
companion object {
fun foo(): String {
return "unmocked"
}
}
}
Intuitively, we might expect to use mockStatic(MyClass.Companion::class.java)
and stub foo()
. But this doesn't work as expected. (Note: if you mark with the method with @JvmStatic
, you can stub the generated static wrapper method but only Java callsites will invoke this.)
We found a way to make stubbing companion object methods work by using mockConstruction
:
mockConstruction(MyClass.Companion::class.java).use {
whenever(MyClass.foo()).thenReturn("Mocked")
val result = MyClass.foo()
assertEquals("Mocked", result)
verify(MyClass).foo()
}
This works because foo()
as compiled is actually an instance method on the inner class MyClass$Companion
. A callsite invoking the method is equivalent to:
val myClassCompanionInstance = MyClass.Companion
myClassCompanionInstance.foo()
where MyClass bytecode is equivalent to (expressed in Java):
class MyClass {
static final Companion Companion;
static {
Companion = new Companion();
}
....
}
Meanwhile, StackOverflow answers recommend refactoring the code to have a wrapper method, or use competing mocking frameworks like MockK or PowerMock:
https://stackoverflow.com/questions/53174839/unit-testing-verifying-a-companion-object-method-is-called-mocking-a-companion
Is using mockConstruction
really the intended way to stub methods on Kotlin companion objects? If so, can you add this guidance to the wiki?