When switching to Kotlin, static methods are moved into a companion object. However, there is no obvious way to unit test other methods which call these "static method".
In Java, we could use PowerMockito's MockStatic(SomeClass.class) to verify a static method is called in the method under test. PowerMock loses its magic in Kotlin.
For testing, I have create the following classes.
public class DummyJava {
static public Void staticMechod(){
System.out.print("Dummy method is called");
return null;
}
}
class DummyCompanion {
companion object {
fun someCompanionMethod(){
System.out.printf("companion method is called\n")
}
}
}
Now there is a another class which calls the DummyCompanion.someCompanion
public class DummyWrapper {
public void callAStaticMethod(){
DummyJava.staticMechod();
}
public void callCompanionMethod(){
DummyCompanion.Companion.someCompanionMethod();
}
}
To unit test callAStaticMethod()
We used the following
@RunWith(PowerMockRunner.class)
@PrepareForTest({DummyJava.class, DummyCompanion.Companion.class})
public class staticClassTest {
//This case works
@Test
public void testForStaticMethod() {
PowerMockito.mockStatic(DummyJava.class);
DummyWrapper testObject = new DummyWrapper();
Mockito.when(DummyJava.staticMechod()).thenCallRealMethod();
testObject.callAStaticMethod();
PowerMockito.verifyStatic(Dummy.class);
DummyJava.staticMechod();
}
//This case doesn't work. It always passes.
@Test
public void testForCompanionMethod() {
PowerMockito.mockStatic(DummyCompanion.Companion.class);
DummyWrapper testObject = new DummyWrapper();
testObject.callCompanionMethod();
PowerMockito.verifyStatic(DummyCompanion.Companion.class,Mockito.times(1));
DummyCompanion.Companion.someCompanionMethod();
}
My question is how to verify the companion method is called.
An object under test may have dependencies on other (complex) objects. To isolate the behavior of the object you want to replace the other objects by mocks that simulate the behavior of the real objects. This is useful if the real objects are impractical to incorporate into the unit test.
Mocking is done when you invoke methods of a class that has external communication like database calls or rest calls. Through mocking you can explicitly define the return value of methods without actually executing the steps of the method.
Mocking is a technique of unit testing a class, where we mock an external dependency in order to test our classes and methods. When unit tests are written well with mocks, they would not have any external dependencies and will not fail when external stuff changes. We looked at the Mockito framework.
companion object is how you define static variables/methods in Kotlin. You are not supposed to create a new instance of Retrofit / ApiService each time you execute a request, however.
Solution 1 : add a caller function in the calling class
public class DummyWrapper {
val foo get() = DummyCompanion.Companion
public void callAStaticMethod(){
foo.staticMechod();
}
public void callCompanionMethod(){
foo.someCompanionMethod();
}
}
In the test class, we can use Mockito to provide a stub for the get()
function and verified it is called.
@Test
fun testCase{
....
val mockCompanionObj: DummyCompanion.Companion = mock()
val wrapper = DummyWrapper()
whenever(wrapper.foo).thenReturn(mockCompanionObj)
wrapper.callCompanionMethod()
verify(mockCompanionObj).someCompanionMethod()
....
}
Solution 2: using Mockk Mocking companion object in Mockk is easy. There is no need to insert a test interfacing object in the source code.
@Test
fun testCompanionObject() {
//Mock the companion object
mockkObject(DummyCompanion.Companion)
//define the stubbing bechavior of a companion object method
every { DummyCompanion.Companion.companionMethod() } answers { stubMethod() }
val testObject = DummyWrapper()
//Call a method that calls the companion object method
//You can verify stubMethod() is called
testObject.callCompanionMethod()
verify(exactly = 1) { DummyCompanion.someCompanionMethod() }
}
For details see Mockk
You can do so with PowerMock too, it'd be like this:
@RunWith(PowerMockRunner.class)
@PrepareForTest({DummyCompanion.class})
public class staticClassTest {
@Test
public void testForCompanionMethod() {
PowerMockito.mockStatic(DummyCompanion.class);
DummyCompanion.Companion companionMock = PowerMockito.mock(DummyCompanion.Companion.class);
Whitebox.setInternalState(
DummyCompanion.class, "Companion",
companionMock
);
DummyWrapper testObject = new DummyWrapper();
testObject.callCompanionMethod();
Mockito.verify(companionMock,Mockito.times(1)).someCompanionMethod();
}
}
Kotlin creates for Java (in the Kotlin class, which is DummyCompanion
in this case) a static field of its Companion subclass named Companion
which can be set using PowerMock's WhiteBox.setInternalState
tool to a mocked Companion
instance that you can later verify method calling to.
Here is one more solution that doesn't require Mockk or PowerMock.
helper
) in the calling class. Now it can be mocked normally in unit tests.interface IStaticHelper {
fun foo(): String
fun bar(): String
}
class StaticHelper {
companion object : IStaticHelper {
override fun foo() = "foo"
override fun bar() = "bar"
}
}
class Caller(private val helper: IStaticHelper = StaticHelper.Companion) {
fun callsTheHelper(): String {
return helper.foo()
}
}
class CallerTest {
@Test
fun testCallsTheHelper() {
val helper = mock()
val caller = Caller(helper)
assertThat(caller.callsTheHelper()).isEqualTo("foo")
}
}
While it's true that the caller in this case is no longer making static method calls, other classes can continue to do so unchanged.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With