jcflorezr
jcflorezr

Reputation: 336

How to test a kotlin suspend function with Spock (groovy)

I have the following kotlin suspending function:

suspend fun registerNewTransaction(transaction: String): String

I am trying to test it through Spock, but I have not found the way to call this kotlin suspending function without specifying the Continuation<? super String> parameter since Groovy identifies the kotlin container class as Java class. I would like to know if it is possible to call suspending functions from another JVM-based language, otherwise I will have to use another testing library like JUnit.

Upvotes: 2

Views: 2158

Answers (4)

jasiustasiu
jasiustasiu

Reputation: 1668

I used this question as a reference Call Kotlin suspend function in Java class and added this function in my code

fun <T> await(block: suspend () -> T): T =
  GlobalScope.future { block.invoke() }.get()

and use it this way:

await {
  myService.mySuspendedFunction(myArguments, it)
}

where it is a kotlin.coroutines.Continuation object

Upvotes: 0

Rohde Fischer
Rohde Fischer

Reputation: 1482

The answer by @julianwki will break once running something like: withContext(Dispatchers.IO) { /* ... /*/ }

A more robust version is to run with an actual CoroutineContext rather than mocking it:

def continuation = Mock(Continuation) {
    getContext() >> Dispatchers.Default
}

I assume it is somehow possible to create a Continuation through calling TestScopeKt.TestScope(EmptyCoroutineContext.INSTANCE) (org.jetbrains.kotlinx:kotlinx-coroutines-test), since that seems to be what what the underlying runTest is using. However, I newer found a good way to reach that point.

Also be aware that mocking Continuation we do not have an implementation for fun resumeWith(result: Result<T>) which so far does not seem to be an issue, but might be in the future. Finding a completely non-mocked solution is probably desirable.

Additionally, if calling a suspend function from Groovy/Spock one might be out of luck. I did try the solutions suggested on: Call Kotlin suspend function in Java class and similar "hacks" without any luck, if calling them directly from Groovy. One might be able to make a Kotlin wrapper for each single call needed, however, in my case at least it is not viable

Upvotes: 1

julianwki
julianwki

Reputation: 422

You can mock that Continuation object, which kotlin is compiling into the method call. This code worked for me:

// mocked continuation
def continuation = Mock(Continuation) {
  getContext() >> Mock(CoroutineContext)
}

// method call which should be tested
subjectUnderTest.suspendableMethod(someParameter, continuation)

Upvotes: 4

Evgeny  Bovykin
Evgeny Bovykin

Reputation: 3069

if it is possible to call suspending functions from another JVM-based language

It's possible, but you need to pass Continuation as last parameter, since it's how Kotlin compiles suspend functions

I'd suggest using Kotlin for testing Kotlin code, there are a lot of Kotlin-specific libraries like mockk that support coroutines.

Upvotes: 0

Related Questions