Search code examples
javaunit-testingkotlin

Unit Testing verifying a companion object method is called (mocking a companion object)


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.


Solution

  • Solution 1 : add a caller function in the calling class

    class DummyWrapper {
      val foo get() = DummyCompanion.Companion
    
      fun callAStaticMethod() {
        foo.staticMethod()
      }
    
      fun 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 behavior of a companion object method
      every { DummyCompanion.someCompanionMethod() } 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