44

How to mock Kotlin extension function using Mockito or PowerMock in tests? Since they are resolved statically should they be tested as static method calls or as non static?

Romper
  • 2,009
  • 3
  • 24
  • 43
  • Just like in Java, you want to avoid static methods like that. Static methods should usually be used as simple util functions. Extension functions are like static methods, so they should only be used as simple util functions. These functions should be so simple it is not needed or worth to mock them. – nhaarman Jun 06 '17 at 06:48
  • can you provide what have you tried till now? – Sachin Chandil Jun 06 '17 at 08:50
  • Just wondering: are you still waiting for other answers; or is there something I could do to make my answer accept-worthy? – GhostCat Aug 03 '17 at 07:13

4 Answers4

55

I think MockK can help you.

It supports mocking extension functions too.

You can use it to mock object-wide extensions:

data class Obj(val value: Int)

class Ext {
    fun Obj.extensionFunc() = value + 5
}

with(mockk<Ext>()) {
    every {
        Obj(5).extensionFunc()
    } returns 11

    assertEquals(11, Obj(5).extensionFunc())

    verify {
        Obj(5).extensionFunc()
    }
}

If you extension is a module-wide, meaning that it is declared in a file (not inside class), you should mock it in this way:

data class Obj(val value: Int)

// declared in File.kt ("pkg" package)
fun Obj.extensionFunc() = value + 5

mockkStatic("pkg.FileKt")

every {
    Obj(5).extensionFunc()
} returns 11

assertEquals(11, Obj(5).extensionFunc())

verify {
    Obj(5).extensionFunc()
}

By adding mockkStatic("pkg.FileKt") line with the name of a package and file where extension is declared (pkg.File.kt in the example).

More info can be found here: web site and github

Demigod
  • 5,073
  • 3
  • 31
  • 49
  • does this work when the extension function has arguments? Verify seems to fail due to mismatching arguments (weirdly it seems to expect another argument, of type equal to the class you're extending) – hmac Nov 27 '19 at 11:18
  • 1
    Works prefectly, thanks! Note when using *verify* you need to use the one from Mockk library and not from Mockito. Since I had both in the same file, I solved it by creating import alias: `import io.mockk.verify as verifyMockk` – Micer Jul 30 '20 at 13:09
  • 1
    This did not work for me, but a slightly different way did (adapted to the example): every { any().extensionFunc() } returns 11 looks silly because now the result does not depend on parameters, but in my use-case it didn't have to and the other way was throwing errors. – LeYAUable Sep 01 '20 at 14:22
10

Instance extension functions can be stubbed and verified like this with the help of mockito-kotlin:

data class Bar(thing: Int)

class Foo {
   fun Bar.bla(anotherThing: Int): Int { ... }
}

val bar = Bar(thing = 1)
val foo = mock<Foo>()

with(foo) {
  whenever(any<Bar>().bla(any()).doReturn(3)
}

verify(foo).apply {
  bar.bla(anotherThing = 2)
}
Thomas Keller
  • 5,933
  • 6
  • 48
  • 80
9

First of all, Mockito knows nothing Kotlin specific language constructs. In the end, Mockito will have a look into the byte code. Mockito is only able to understand what it finds there and what looks like a Java language construct.

Meaning: to be really sure, you might want to use javap to deassemble the compiled classfiles to identify the exact names/signatures of the methods you want to mock.

And obviously: when that method is static, you have to user PowerMock, or JMockit; if not, you should prefer to with Mockito.

From a java point of view, you simply avoid mocking static stuff; but of course, things get really interesting, now that different languages with different ideas/concepts come together.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • 8
    just for the next person, extension function signatures in java: `class FilenameKt { public static ReturnType extensionName(Receiver receiver, Params otherParams)...` – Kraiden Jul 20 '17 at 05:10
3

I use mockk library.

For extension file write java name, like this:

@file:JvmName(name = "ExtensionUtils")

package myproject.extension

...

And for fast codding I created file with different extension mocks:

object FastMock {

    fun extension() = mockkStatic("myproject.extension.ExtensionUtils")

    fun listExtension() = mockkStatic("myproject.extension.ListExtensionUtils")

}

In test call this:

FastMock.listExtension()
every { itemList.move(from, to) } returns Unit
SerjantArbuz
  • 982
  • 1
  • 12
  • 16