mockk: Bug: callOriginal() for default interface methods is not working

import io.mockk.every
import io.mockk.mockk
import io.mockk.spyk
import io.mockk.verify
import javax.xml.crypto.Data

data class ItemData(val id: Int)

interface Database {
    fun insertItems(items: List<ItemData>)
    fun deleteItems(items: List<ItemData>)
    fun runTransaction(alsoDelete: Boolean) {
        insertItems(listOf(ItemData(id = 1), ItemData(id = 2), ItemData(id = 3)))
        if (alsoDelete)
            deleteItems(listOf(ItemData(id = 4), ItemData(id = 5), ItemData(id = 6)))
    }
}


fun main(args: Array<String>) {
    val x = mockk<Database>()
    every { x.runTransaction(any()) } answers { callOriginal() }
    x.runTransaction(alsoDelete = true)
    verify { x.insertItems(listOf(ItemData(id = 1), ItemData(id = 2), ItemData(id = 3))) }
}

Should work

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 10
  • Comments: 15 (1 by maintainers)

Most upvoted comments

This is a HUGE blocker, making testing default functions if not impossible then at least horribly complex instead of straight-forward. It was reported over 3 years ago and not even a feedback available here. What the …?

quick update, a much cleaner option works with abstract classes:


import io.mockk.*

data class ItemData(val id: Int)

interface Database {
    fun insertItems(items: List<ItemData>)
    fun deleteItems(items: List<ItemData>)
    fun runTransaction(alsoDelete: Boolean) {
        insertItems(listOf(ItemData(id = 1), ItemData(id = 2), ItemData(id = 3)))
        if (alsoDelete)
            deleteItems(listOf(ItemData(id = 4), ItemData(id = 5), ItemData(id = 6)))
    }
}


fun main(args: Array<String>) {
//    val x = mockk<Database>() // this should work, but doesn't due to https://github.com/mockk/mockk/issues/64

    // instead we create an abstract class to work around the issue.
    abstract class DatabaseMock: Database
    val x = mockk<DatabaseMock>()
    
    // the members/methods of the interface need to be stubbed too:
    every { x.insertItems(any()) } just Runs
    every { x.deleteItems(any()) } just Runs

    every { x.runTransaction(any()) } answers { callOriginal() }
    x.runTransaction(alsoDelete = true)
    verify { x.insertItems(listOf(ItemData(id = 1), ItemData(id = 2), ItemData(id = 3))) }
}

This is a problem caused by Mockk not being able to reliably spy interfaces. The solution I am using is also by creating an in-place abstract class “implementing” the interface I want to test, but instead of mockk-ing it, I use spyk call on the abstract class, that way i do not need to declare the callOriginal() on methods I want to actually test.

Sorry about the delay in the release, planning to make one next week.

@flapenna : Is this issue fixed by above PR?

It should, but the fix hasn’t been released yet

Hello everyone! I was encountered with this too and I wish to fix it 😃 So, I can start to fix it but are there any clues or advice about it?