Error reporting solution of Mockito mock Kotlin Object method

  • 2021-11-13 01:47:51
  • OfStack

For example, if I create a Kotlin Object Class: ObjectMethod


package com.baichuan.example.unit_test

object ObjectMethod {

    fun doSomething() {
        println("this is ObjectMethod#doSomething")
    }

    @JvmStatic
    fun doSomethingWithJvmStatic() {
        println("this is ObjectMethod#doSomethingWithJvmStatic")
    }
}

If I go straight to mock Of this class doSomething Method, will report an error.


  @Test
  @DisplayName("mock Ordinary kotlin Static method ")
  fun testMockKotlinObject() {
      Assertions.assertThrows(MissingMethodInvocationException::class.java) {
          Mockito.mockStatic(ObjectMethod::class.java).`when`<Unit>(
              ObjectMethod::doSomething
          ).thenAnswer { println("this is mocked Object#doSomething") }
      }

      ObjectMethod.doSomething()
  }

This is because kotlin In object Although the methods in the class are in the kotlin From the point of view of form and use, there is no static method 2. But compiled into java After the code, its essence is to initialize a static constant instance of the current class internally INSTANCE . This INSTANCE In kotlin It is hidden in syntax, but it can still display access in java. ObjectMethod Compile into java The code after this is as follows:


public final class ObjectMethod {
   @NotNull
   public static final ObjectMethod INSTANCE = new ObjectMethod();

   private ObjectMethod() {
   }

   public final void doSomething() {
      String var1 = "this is ObjectMethod#doSomething";
      boolean var2 = false;
      System.out.println(var1);
   }

   @JvmStatic
   public static final void doSomethingWithJvmStatic() {
      String var0 = "this is ObjectMethod#doSomethingWithJvmStatic";
      boolean var1 = false;
      System.out.println(var0);
   }
}

So, you can't mock ObjectMethod#doSomething The essential reason is that normal means can't mock Static constant. If you want to make kotlin Adj. object Methods in the class can be mock Simply add to the method @JvmStatic Annotations will do. The methods annotated by it will be compiled into ordinary java Static method.

It is said above that normal means cannot be mock static constant, so what about abnormal means? In fact, this abnormal means is that through reflection, it will be mock The passed instance is injected into the ObjectMethod Just medium.


	@Test
	@DisplayName(" Modify static constants by reflection to mock Ordinary kotlin Static method ")
	fun testMockKotlinObjectMethodByReflection() {
	    val mock = Mockito.mock(ObjectMethod::class.java)
	    Mockito.`when`(mock.doSomething()).then {
	        print("this is mocked ObjectMethod by reflection")
	    }
	    val declaredMethod = ObjectMethod::class.java.getDeclaredField("INSTANCE")
	    ReflectionUtils.setFinalStatic(declaredMethod, mock)
	
	    ObjectMethod.doSomething()
	}

ReflectionUtils


package com.baichuan.example.unit_test

import java.lang.reflect.Field
import java.lang.reflect.Modifier

object ReflectionUtils {
    @Throws(Exception::class)
    fun setFinalStatic(field: Field, newValue: Any) {
        field.isAccessible = true
        val modifiersField: Field = Field::class.java.getDeclaredField("modifiers")
        modifiersField.isAccessible = true
        modifiersField.setInt(field, field.modifiers and Modifier.FINAL.inv())
        field.set(null, newValue)
    }
}

github

https://github.com/scientificCommunity/blog-sample/tree/main/unit-test-sample


Related articles: