Mocking and Kotlin
One of the good things with Kotlin is it’s accessible interoperability with Java. Being a new language though it has made some design decisions that are different from Java’s which sometimes can create boundaries in the usage across language boundaries and one of these decisions is what I want to talk about today.
In Java every class, and every function in this class, is by default extendible. You have to explicitly state that it is sealed
to make it non extendible. Now, in Kotlin they wanted another route. They chose to say that nothing is extendible unless you explicitly mark it as open
. Although I can’t look into the head of the language designers I believe this choice was made because inheritance in general is something that can easily backfire, and with the feature of Extension Methods
there really isn’t too great of a need of overwriting unexpected methods.. Unless you happen to be Mockito and want to mock something.
Baeldung has written an Article on mocking and mockito. It uses an Interface
though which of course is extendible. If you want to mock a class you have to make sure that the class is open
and the function you want to extend in open
. What happens otherwise? If the class isn’t open
you’ll get a straight up exception saying it can’t be extended. If the method is not open
you won’t get an exception, but the method
ends up not being mocked, meaning it will try to run the real code, which in a mock proxy likely can end up with a NullPointerException
since no instance variables will have been initialize by default.
Code of the Day
To help you with things that needs to be proxied (like for example mocks, but also Controllers
or other framework code that wants to do some magic), Gradle
has some plugin support that will automatically mark your class and its methods as open
.
plugins {
id 'org.jetbrains.kotlin.jvm' version "1.5.0"
id "org.jetbrains.kotlin.plugin.allopen" version "1.5.0"
}
...
allOpen {
annotation("javax.ws.rs.Path")
annotation("javax.enterprise.context.ApplicationScoped")
annotation("io.quarkus.test.junit.QuarkusTest")
}
The above are excerpts from a build.gradle
file. Classes that are marked with the mentioned Annotations
in allOpen
will automatically get all functions marked as open
. This is needed by Quarkus
to be able to manage beans. If we would have been coding Spring
we would likely have put the Component
annotation in here.