Skip to content

Commit

Permalink
Merge pull request #785 from npars/mock-invisible-parent
Browse files Browse the repository at this point in the history
Support mocking non-accessible parent methods
  • Loading branch information
Raibaz committed Feb 7, 2022
2 parents c9c842b + 9741290 commit 5a4ccff
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 1 deletion.
9 changes: 8 additions & 1 deletion dsl/jvm/src/main/kotlin/io/mockk/InternalPlatformDsl.kt
Expand Up @@ -7,10 +7,12 @@ import java.lang.reflect.Method
import java.util.concurrent.atomic.AtomicLong
import kotlin.coroutines.Continuation
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.KProperty1
import kotlin.reflect.KType
import kotlin.reflect.KTypeParameter
import kotlin.reflect.full.allSuperclasses
import kotlin.reflect.full.functions
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
Expand Down Expand Up @@ -100,7 +102,7 @@ actual object InternalPlatformDsl {
anyContinuationGen: () -> Continuation<*>
): Any? {
val params = arrayOf(self, *args)
val func = self::class.functions.firstOrNull {
val func = self::class.allAncestorFunctions().firstOrNull {
if (it.name != methodName) {
return@firstOrNull false
}
Expand Down Expand Up @@ -134,6 +136,11 @@ actual object InternalPlatformDsl {
}
}

private fun KClass<*>.allAncestorFunctions(): Sequence<KFunction<*>> {
return (sequenceOf(this) + this.allSuperclasses.asSequence())
.flatMap { it.functions }
}

private fun List<KType>.anyIsInstance(value: Any?): Boolean {
return any { bound ->
val classifier = bound.classifier
Expand Down
66 changes: 66 additions & 0 deletions mockk/jvm/src/test/kotlin/io/mockk/it/PrivateParentMethodTest.kt
@@ -0,0 +1,66 @@
package io.mockk.it

import io.mockk.every
import io.mockk.mockk
import io.mockk.spyk
import io.mockk.verify
import kotlin.test.Test
import kotlin.test.assertEquals

/**
* Test mocking non-visible parent methods through dynamic calls. Issue #425
*/
class PrivateParentMethodTest {
open class Parent {
open fun call(): String = callPrivate()
private fun callPrivate() = "Real"
}

open class Child: Parent()

class ChildWithShadowedMethod: Parent() {
override fun call(): String = callPrivate()
fun callPrivate() = "Shadowed"
}

class GrandChild: Child()

@Test
fun testChildAlwaysMockedFirst() {
val mock = mockk<ChildWithShadowedMethod> {
every { call() } answers { callOriginal() }
every { this@mockk["callPrivate"]() } returns "Mock"
}

assertEquals(mock.call(), "Mock")
}

@Test
fun testPrivateCallMock() {
val mock = mockk<Child> {
every { call() } answers { callOriginal() }
every { this@mockk["callPrivate"]() } returns "Mock"
}

assertEquals(mock.call(), "Mock")
}

@Test
fun testPrivateCallMockForGrandChild() {
val mock = mockk<GrandChild> {
every { call() } answers { callOriginal() }
every { this@mockk["callPrivate"]() } returns "Mock"
}

assertEquals(mock.call(), "Mock")
}

@Test
fun testPrivateCallVerify() {
val mock = spyk(Child(), recordPrivateCalls = true)

mock.call()

verify { mock["callPrivate"]() }
}
}

0 comments on commit 5a4ccff

Please sign in to comment.