Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
oleksiyp opened this issue Apr 23, 2018 · 15 comments
Open

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

oleksiyp opened this issue Apr 23, 2018 · 15 comments

Comments

@oleksiyp
Copy link
Collaborator

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

@oleksiyp oleksiyp added the bug label Apr 23, 2018
@oleksiyp oleksiyp changed the title callOriginal() is not working Bug: callOriginal() is not working May 16, 2018
@oleksiyp oleksiyp changed the title Bug: callOriginal() is not working Bug: callOriginal() for default interface methods is not working May 16, 2018
@fargus9
Copy link

fargus9 commented Aug 8, 2018

This is blocking some of my tests as well...

@oleksiyp oleksiyp added this to To do in MockK features Dec 25, 2018
@jsingle
Copy link

jsingle commented Apr 4, 2019

Same here

@stale
Copy link

stale bot commented Jul 23, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. If you are sure that this issue is important and should not be marked as stale just put an important tag.

@stale stale bot added stale and removed stale labels Jul 23, 2019
@oleksiyp oleksiyp removed this from To do in MockK features Nov 2, 2019
@oleksiyp oleksiyp added this to To do in Critical to fix Nov 2, 2019
@munnitz
Copy link

munnitz commented May 30, 2021

What's the status on this one? I tried using this when mocking an interface like so

class InterfaceMock : Interface by mockk() {
    init {
        coEvery { defaultSuspendFunctionCall(any()) } coAnswers { callOriginal() }
    }
}

but it does not seem to work. Am I using it incorrectly or is it still not fixed?

@rafalwrzeszcz
Copy link

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 …?

@juniofernandes-hotmart
Copy link

Same issue here. Any feedbacks?

@dugsmith
Copy link

I'm also blocked writing a test like this because of this problem. Any plans to fix it?

@aivinog1
Copy link

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?

@zakhenry
Copy link

I've banged my head against this for a while and found an workaround. Annoying but workable in the interim:

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 anonymous object that implements the interface (but doesn't actually implement it; we stub the methods)
    val x = spyk(object: Database {
        override fun deleteItems(items: List<ItemData>) {}
        override fun insertItems(items: List<ItemData>) {}
    })
    // the members/methods of the interface can be stubbed if needed: 
//    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))) }
}

@zakhenry
Copy link

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))) }
}

@ondrej-simon
Copy link

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.

@levinotik
Copy link

Ran into this as well today. I resolved with @zakhenry's workaround, but it'd be great if this could be fixed. Makes tests a bit ugly and confusing for others when they see this empty abstract class sitting there.

@goro-7
Copy link

goro-7 commented Feb 8, 2024

@flapenna : Is this issue fixed by above PR?

@flapenna
Copy link
Contributor

flapenna commented Feb 8, 2024

@flapenna : Is this issue fixed by above PR?

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

@Raibaz
Copy link
Collaborator

Raibaz commented Feb 16, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

No branches or pull requests