As we probably know, by default Kotlin class once defined, it is final, unless it is explicitly declared open
.
This would post a challenge when we want to Mock it using Mockito. We need to explicitly declare it as open
. Is there a way we could avoid declaring it as open
while able to Mock it for our testing?
Mockito2 can now mock final classes as well.
However, this feature is opt-in, so you need to enable it manually.
To do so, you need to define a file /mockito-extensions/org.mockito.plugins.MockMaker
containing the line mock-maker-inline
See e.g.
http://hadihariri.com/2016/10/04/Mocking-Kotlin-With-Mockito/ or
https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#unmockable
for a quick introduction
on a side note, this currently doesn't work for android
There're three ways I'm aware of how you can mock Kotlin classes:
Use interfaces instead of classes. In this case you replace all usages of a particular class with the corresponding interface. And in testing code you mock the interface.
interface Something { /* ... */ }
class SomethingImpl : Something { /* ... */ }
fun processSomething(something: Something) { /* ... */ }
val something = mock(Something::class.java)
processSomething(mock)
Make classes open, which is not very convenient.
Use PowerMock instead of Mockito. Using its ClassLoader
you can do much more than with Mockito.
I prefer the first approach because it's a good idea to work with interfaces instead of classes even if you don't use mocking frameworks.
The MockMaker plugin doesn't seem to work when running espresso tests. Therefore, you can use Kotlin's all-open pugin instead.
Add the plugin in build.gradle:
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
}
}
apply plugin: "kotlin-allopen"
Specify the annotation that will make the class open:
allOpen {
annotation("com.my.MyMockable")
}
Create your annotation which can be used to annotate classes:
@Target(AnnotationTarget.CLASS)
annotation class MyMockable
Then, in order to make your class and its public methods Mockable (open), annotate it with your annotation:
@MyMockable