Introduction to the

As the Kotlin language becomes more and more popular in Android development, there are naturally all sorts of problems.

This article is mainly about my thoughts and solutions to some of the problems I encountered in the Android unit test of the Kotlin class.

Problems encountered

We all know that Kotlin has a lot of syntax candy for developers, one of which is the top-level function. We can put the function directly at the top of the code file, so that it doesn’t belong to any class.

It’s easy to use, just call it as a normal function from anywhere in the Kotlin code, whereas in Java you need to call it with the class name of filename +Kt as if it were static (default configuration)

In Java unit tests, if you want to mock the top-level function, you simply use the mockStatic method as if it were a static method

In the Kotlin unit test, we couldn’t find this class

Determine the route

We create a file to write a top-level function, and a unit test class to test it:

As you can see from above, kotlin’s top-level function actually becomes a static method wrapped in a class when compiled. We can verify this simply:

In Android Studio, click Tools->Kotlin->Show Kotlin ByteCode on the menu to pop up the ByteCode for the corresponding class, and click the Decompile button to see that a static method is actually compiled into a class

Once that’s done, we just need to get the class of the top-level function in Kotlin and use mockStatic as we do in Java.

The analysis process

Since we’re talking about runtime type analysis, reflection comes naturally, and we’ll start with Kotlin’s reflection library

implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

I’m not familiar with Kotlin’s reflection, so I looked it up in the documentation and found ::sampleTopFun. Its return value is an interface class called KFunction. Let’s see what methods it can call

There seems to be no approach to our requirements, so what do we do? Let’s take a look at its implementation class, maybe there are some private variables that hold the information we need.

So how do you find the implementation class? Parsing the intricacies of source code directly is time-consuming and inefficient, so I took a tricky approach here, using Android Studio’s Debug feature:

Contrary to expectations, why is the type we get here such a bizarre thing? Let’s look at the bytecode of this file

Looked down again

We find that this weird type is generated automatically after Kotlin is compiled and inherits from FunctionReference, and in the Debugger we get an important piece of information: KFunctionImpl

According to the name guess, it should be where the real function of KFunction is implemented, we will expand its information

As you can see, we have found the class we want. Once we get it, we can mock it easily

To start the Mock

From above, we already know the path to the jClass we need to get

We first obtain the reflected referenced KFunctionImpl from FunctionReference, which is actually a variable in CallableReference inherited by FunctionReference. In FunctionReference there is a getReflected method that is called with reflection to get the object. Of course, you can get it with reflection Field, but notice that getReflected methods handle empty objects. Just to be on the safe side, Let’s go back to the reflection call getReflected method to get KFunctionImpl

Reflection calls the getReflected method to get the KFunctionImpl

The first step is ok, then reflection to get the Container

The second step is ok, and then reflection gets the jClass, right

Ok, that’s fine, so just like Java, let’s try out mockStatic, the class we got

As you can see, the test passed successfully, so we have successfully solved the Mockito problem of simulating the top-level function. For ease of use, you can wrap the above code into a function that you won’t repeat here.