diff --git a/agent/android/build.gradle b/agent/android/build.gradle index 13ef4a181..825a76d82 100644 --- a/agent/android/build.gradle +++ b/agent/android/build.gradle @@ -80,9 +80,9 @@ android { dependencies { api project(':mockk-agent-api') api project(':mockk-agent-common') - api "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - api "com.linkedin.dexmaker:dexmaker:$dexmaker_version" - api "org.objenesis:objenesis:$objenesis_version" + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation "com.linkedin.dexmaker:dexmaker:$dexmaker_version" + implementation "org.objenesis:objenesis:$objenesis_version" androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.2', { exclude group: 'com.android.support', module: 'support-annotations' }) diff --git a/dsl/common/src/main/kotlin/io/mockk/API.kt b/dsl/common/src/main/kotlin/io/mockk/API.kt index c9ad575b9..879276584 100644 --- a/dsl/common/src/main/kotlin/io/mockk/API.kt +++ b/dsl/common/src/main/kotlin/io/mockk/API.kt @@ -683,7 +683,9 @@ open class MockKMatcherScope( inline fun any(): T = match(ConstantMatcher(true)) inline fun capture(lst: MutableList): T = match(CaptureMatcher(lst, T::class)) inline fun capture(lst: CapturingSlot): T = match(CapturingSlotMatcher(lst, T::class)) - inline fun captureNullable(lst: MutableList): T? = match(CaptureNullableMatcher(lst, T::class)) + inline fun captureNullable(lst: MutableList): T? = + match(CaptureNullableMatcher(lst, T::class)) + inline fun > cmpEq(value: T): T = match(ComparingMatcher(value, 0, T::class)) inline fun > more(value: T, andEquals: Boolean = false): T = match(ComparingMatcher(value, if (andEquals) 2 else 1, T::class)) @@ -3631,6 +3633,7 @@ data class MethodDescription( val returnsUnit: Boolean, val returnsNothing: Boolean, val isSuspend: Boolean, + val isFnCall: Boolean, val declaringClass: KClass<*>, val paramTypes: List>, val varArgsArg: Int, diff --git a/mockk/common/src/main/kotlin/io/mockk/impl/recording/ChainedCallDetector.kt b/mockk/common/src/main/kotlin/io/mockk/impl/recording/ChainedCallDetector.kt index 158041327..dddf75630 100644 --- a/mockk/common/src/main/kotlin/io/mockk/impl/recording/ChainedCallDetector.kt +++ b/mockk/common/src/main/kotlin/io/mockk/impl/recording/ChainedCallDetector.kt @@ -6,6 +6,7 @@ import io.mockk.InternalPlatformDsl.toStr import io.mockk.impl.InternalPlatform import io.mockk.impl.log.Logger import io.mockk.impl.log.SafeToString +import kotlin.coroutines.Continuation class ChainedCallDetector(safeToString: SafeToString) { val log = safeToString(Logger()) @@ -119,7 +120,15 @@ class ChainedCallDetector(safeToString: SafeToString) { @Suppress("UNCHECKED_CAST") fun buildRecordedCall(): RecordedCall { - if (zeroCall.method.isSuspend) { + fun SignedCall.isSuspend() = when { + method.isSuspend -> true + method.isFnCall -> args.lastOrNull()?.let { + Continuation::class.isInstance(it) + } ?: false + else -> false + } + + if (zeroCall.isSuspend()) { log.trace { "Suspend function found. Replacing continuation with any() matcher" } argMatchers[argMatchers.size - 1] = ConstantMatcher(true) } diff --git a/mockk/common/src/main/kotlin/io/mockk/impl/recording/WasNotCalled.kt b/mockk/common/src/main/kotlin/io/mockk/impl/recording/WasNotCalled.kt index 3a143370b..846007485 100644 --- a/mockk/common/src/main/kotlin/io/mockk/impl/recording/WasNotCalled.kt +++ b/mockk/common/src/main/kotlin/io/mockk/impl/recording/WasNotCalled.kt @@ -9,6 +9,7 @@ object WasNotCalled { true, false, false, + false, Unit::class, listOf(), -1, diff --git a/mockk/common/src/test/kotlin/io/mockk/it/AnswersTest.kt b/mockk/common/src/test/kotlin/io/mockk/it/AnswersTest.kt index 612b81fb1..8b4ce840f 100644 --- a/mockk/common/src/test/kotlin/io/mockk/it/AnswersTest.kt +++ b/mockk/common/src/test/kotlin/io/mockk/it/AnswersTest.kt @@ -1,3 +1,5 @@ +@file:Suppress("UNUSED_PARAMETER") + package io.mockk.it import io.mockk.* diff --git a/mockk/jvm/src/main/kotlin/io/mockk/impl/instantiation/JvmMockFactoryHelper.kt b/mockk/jvm/src/main/kotlin/io/mockk/impl/instantiation/JvmMockFactoryHelper.kt index 7bbb07298..6ab1ebb86 100644 --- a/mockk/jvm/src/main/kotlin/io/mockk/impl/instantiation/JvmMockFactoryHelper.kt +++ b/mockk/jvm/src/main/kotlin/io/mockk/impl/instantiation/JvmMockFactoryHelper.kt @@ -131,6 +131,8 @@ object JvmMockFactoryHelper { } ?: false } + val isFnCall = Function::class.java.isAssignableFrom(declaringClass) + val returnType = kotlinFunc?.returnType as? KClass<*> ?: returnType.kotlin val result = MethodDescription( @@ -139,6 +141,7 @@ object JvmMockFactoryHelper { returnTypeIsUnit, returnTypeIsNothing, isSuspend, + isFnCall, declaringClass.kotlin, parameterTypes.map { it.kotlin }, vararg, diff --git a/mockk/jvm/src/test/kotlin/io/mockk/gh/Issue288Test.kt b/mockk/jvm/src/test/kotlin/io/mockk/gh/Issue288Test.kt new file mode 100644 index 000000000..a1fa9c6ec --- /dev/null +++ b/mockk/jvm/src/test/kotlin/io/mockk/gh/Issue288Test.kt @@ -0,0 +1,17 @@ +package io.mockk.gh + +import io.mockk.coEvery +import io.mockk.mockk +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import kotlin.test.Test +import kotlin.test.assertEquals + +class Issue288Test { + @Test + fun suspendFnMocking(): Unit { + val call = mockk Int>() + coEvery { call() } returns 5 + runBlocking { assertEquals(5, call()) } + } +} \ No newline at end of file