paint-brush
Mocking static method in Kotlinby@ibosz
9,213 reads
9,213 reads

Mocking static method in Kotlin

by I'Boss PotiwarakornJuly 5th, 2018
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Static method is bad, or at least, when it’s not <strong>pure</strong>.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Mocking static method in Kotlin
I'Boss Potiwarakorn HackerNoon profile picture

Photo by Susan Yin on Unsplash

Static method is bad, or at least, when it’s not pure.

Pure, in this context, means that:

  • It causes no side-effects, mutates nothing outside of the method scope.
  • It is referential transparent, meaning any reference must be passed as an argument or else mutation of your referenced value will results in non-deterministic result.

Why is that bad?

Because whoever consumes that impure static method will not be easy to test. If the static method is pure it might be fine since you can predict its behavior and you don’t need to mock them (less mocking is great!), just asserting the return value is enough. But the impure one, it’s a different story.

If the code is under your control, you can and should avoid impure static method. But if you have to use static method from a library or framework and you can’t avoid them, what can you do?

For example, we want to send sms notification using Twillo.

Twillo SDK provides static method Message.creator which is a factory method for constructing MessageCreator. The method that actually makes a request to Twillo to send sms is messageCreator.create(restClient).

Even though Message.creator is pure, but messageCreator.create(restClient) which is the method of the result of Message.creator is not. So we need to mock the create method by first stubbing return value of Message.creator. But since Message.creator is static, we don’t have our way in.

One might say: we can create a wrapper class around that static method, so that we can mock the wrapper instead.

But testing is all about increasing confidence in your code. Adding another level of indirection just for testing doesn’t increase confidence level in your system under test and adding more complexity to your code base.

So if adding wrapper helps you improving team’s coding experience consuming the API and it is being use a lot then it might worth investment, not just for the sake of testing.

Then, is there a leaner way?

Higher-order function FTW

We want to somehow create a path to replace Message.creator with something else. In Kotlin and a many languages these days treat function as a first-class citizen. The smallest reusable component is no more class but function. So you can pass function around like any other object.

In a strict functional and mathematical sense, function must be pure. But in Kotlin context, seems like function is allowed to be impure.

The difference between method and function in Kotlin is, while method is associated to an object, function doesn’t need to. You can see that the fun keyword which refers to function everywhere regardless of being a method declaration or top-level function declaration, which means function is a super-set of method, at least in this context.

But what is Higher-order function?

Higher-order function is basically means the function that has function as parameter(s) or returns function.

Even though function that does not have function as parameter(s) and not returning function is called first-order function, the term higher-order is being used here instead of second-order or third-order because, in theory, we can nest “function that has function as parameter(s) or returns function” infinitely many level. So that it’s higher than second or third order.

Let’s get back to our main topic. So we want to inject Message.creator into notify and we can achieve that by making notify a higher-order function so that we can pass Message.creator in as an argument.

In order to do that, first step, we need get the type right.

Let’s figure out the type of this particular function. Here is the implementation of the original creator in the SDK:

So it has a type

(PhoneNumber, String, String) -> MessageCreator

which means parameter types on the left side of the arrow “->” and return type on the right side.

Now let’s define our typealias to refer to this function type and name it for readability.

But now, we’ve just changed our interface, which is not really nice since normally we don’t need to pass creator to notify except in test.

In Kotlin, we can avoid that by providing default argument.

So now, our existing interface stays the same and we have our way in!

Let’s mock!

I’m using Spek and mockito-kotlin here.

So we define a creator that returns a mocked MessageCreator that stub create method to return Message we want.

You might ask, at line 22:

  • Why don’t we just use Message constructor? => It’s private.
  • Why don’t we mock getters? => It’s final.

So we end up using fromJson instead.

Edit 14 July 2018: David Kirchner point out that mockito 2 can mock _final_ class and method already. See full response here

And I reference to creator function by using ::creator, it will regard as a value. The other way, we can use lambda and assign it to val instead of fun.

By using lambda, we can just ignore the arguments using _ since we are not using it in this context. This is not possible if we define it with fun.

For more info about how all these functions and lambdas work, you can find out in Kotlin documentation.

Now, we might want to verify if the creator is being called with correct arguments. The problem is mockito’s verify only supports method call verification. How can we verify creator that is not associate to any object?

But turns out, function is actually just a class with invoke method, so instead of creating function on our own, we can mock the function type. That way, mockito can verify the invoke method of that function if it’s being called by correct arguments. Here is how function being defined in it’s standard library.

Now we know that we can mock our function to verify the call and we can also stub the return value. Let’s do both!

As you can see, we can verify our function call and stubbing the return value of the static method without hacking the language.

Summary

We are leveraging higher-order function and default argument feature of the language to allow us to inject static method into a function that we want to test so that we can mock/stub it while retaining the old interface. Any language that has both features can also use the same trick as well.

I hope you’ve got the idea.

Thanks for reading! Enjoy testing :D

Follow me on twitter @ibossptk