Is it possible to use Mockito with Kotlin without

2019-02-21 12:45发布

问题:

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?

回答1:

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



回答2:

There're three ways I'm aware of how you can mock Kotlin classes:

  1. 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)
    
  2. Make classes open, which is not very convenient.

  3. 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.



回答3:

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