16

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?

Michael
  • 53,859
  • 22
  • 133
  • 139
Elye
  • 53,639
  • 54
  • 212
  • 474

3 Answers3

11

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
Cristan
  • 12,083
  • 7
  • 65
  • 69
  • 2
    I liked this solution for being the least intrusive and the one that offers most control over the alternatives. – Fabio Jul 10 '17 at 11:33
10

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

Lovis
  • 9,513
  • 5
  • 31
  • 47
  • Is it working with dexmaker? For Android Instrumentation tests? – Aleksei Potapkin Dec 12 '16 at 14:36
  • @folkyatina no idea, sorry – Lovis Dec 13 '16 at 13:39
  • This doesn't work for me either, and is apparently not supported for Android: https://github.com/mockito/mockito/pull/648 – amitavk Aug 21 '17 at 06:57
  • @amitav13 yeah unfortunately. however, the question is not tagged `android`. but I'll update the answer to make this more clear – Lovis Aug 21 '17 at 10:00
  • For Android Instrumentation tests we can use [dexopener](https://github.com/tmurakami/dexopener), which opens final classes without necessity to modify them or add annotations. – stedi Mar 29 '18 at 09:43
8

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.

Michael
  • 53,859
  • 22
  • 133
  • 139
  • Thanks Michael. So for every class we need to test, we need to implement an interface instead? That sounds like more code change than 'open' the class, Or did I miss something... – Elye Apr 11 '16 at 09:56
  • Sure, but it's a big step towards a better architecture :) – Michael Apr 11 '16 at 10:40
  • 5
    Creating a separate interface without a valid reason (from an OO or API design standpoint) is over-enginneering as it only adds complexity. It's even worse than making a class unnecessarily `open`. (And, no, not creating the interface does *not* violate GoF´s principle of "program to an interface, not an implementation".) – Rogério Apr 11 '16 at 15:13
  • 2
    The question is too general to have a single correct answer, but in my opinion classes that need to be mocked are usually injected as dependencies and have other dependencies injected into them. In this case creating a separate interface for such classes is a good idea as it helps to make the system loosely coupled and unit testing easier or even possible. That's quite a valid reason for me. – Michael Apr 11 '16 at 15:28
  • 2
    It's getting a bit off-topic but wanted to point out that using interfaces does not necessarily reduce coupling. You always rely on the contract. If you simply move all public methods to an interface, you do not affect coupling, you just make more code lines. A class has an interface + implementation but when you pass an object around, it's just the interface (_the contract_) that everything relies on. – Pijusn May 26 '16 at 04:23
  • There is another option: You can use [all-open compiler plugin from kotlin](https://engineering.21buttons.com/mocking-kotlin-classes-with-mockito-the-fast-way-631824edd5ba) in your test builds. – Brais Gabin Mar 07 '18 at 17:23