How to unit-test Kotlin-JS code with coroutines? - kotlin

I've created a multi-platform Kotlin project (JVM & JS), declared an expected class and implemented it:
// Common module:
expect class Request(/* ... */) {
suspend fun loadText(): String
}
// JS implementation:
actual class Request actual constructor(/* ... */) {
actual suspend fun loadText(): String = suspendCoroutine { continuation ->
// ...
}
}
Now I'm trying to make a unit test using kotlin.test, and for the JVM platform I simply use runBlocking like this:
#Test
fun sampleTest() {
val req = Request(/* ... */)
runBlocking { assertEquals( /* ... */ , req.loadText()) }
}
How can I reproduce similar functionality on the JS platform, if there is no runBlocking?

Mb it's late, but there are open issue for adding possibility to use suspend functions in js-tests (there this function will transparent convert to promise)
Workaround:
One can define in common code:
expect fun runTest(block: suspend () -> Unit)
that is implemented in JVM with
actual fun runTest(block: suspend () -> Unit) = runBlocking { block() }
and in JS with
actual fun runTest(block: suspend () -> Unit): dynamic = promise { block() }

TL;DR
On JS one can use GlobalScope.promise { ... }.
But for most use cases the best option is probably to use runTest { ... } (from kotlinx-coroutines-test), which is cross-platform, and has some other benefits over runBlocking { ... } and GlobalScope.promise { ... } as well.
Full answer
I'm not sure what things were like when the question was originally posted, but nowadays the standard, cross-platform way to run tests that use suspend functions is to use runTest { ... } (from kotlinx-coroutines-test).
Note that in addition to running on all platforms, this also includes some other features, such as skipping delays (with the ability to mock the passage of time).
If for any reason (which is not typical, but might sometimes be the case) it is actually desirable to run the code in the test as it runs in production (including actual delays), then runBlocking { ... } can be used on JVM and Native, and GlobalScope.promise { ... } on JS. If going for this option, it might be convenient to define a single function signature which uses runBlocking on JVM and Native, and GlobalScope.promise on JS, e.g.:
// Common:
expect fun runTest(block: suspend CoroutineScope.() -> Unit)
// JS:
#OptIn(DelicateCoroutinesApi::class)
actual fun runTest(block: suspend CoroutineScope.() -> Unit): dynamic = GlobalScope.promise(block=block)
// JVM, Native:
actual fun runTest(block: suspend CoroutineScope.() -> Unit): Unit = runBlocking(block=block)

I was able to make the following work:
expect fun coTest(timeout: Duration = 30.seconds, block: suspend () -> Unit): Unit
// jvm
actual fun coTest(timeout: Duration, block: suspend () -> Unit) {
runBlocking {
withTimeout(timeout) {
block.invoke()
}
}
}
// js
private val testScope = CoroutineScope(CoroutineName("test-scope"))
actual fun coTest(timeout: Duration, block: suspend () -> Unit): dynamic = testScope.async {
withTimeout(timeout) {
block.invoke()
}
}.asPromise()
This launches a co-routine in a scope of your choice using async which you can then return like a promise.
You then write a test like so:
#Test
fun myTest() = coTest {
...
}

Related

Kotlin coroutine suspend and delay

I want to simulate file loading and I want to delay code for 4 seconds and I can't do this.
suspend fun showLoadingProgress() : String = suspendCancellableCoroutine{ continuation ->
while (fileIsBeingLoaded())
{
delay(4000)
val percent = ((loadedBites.toDouble() / fileBites.toDouble())*100).toInt()
continuation.resume("$loadedBites/$fileBites ($percent%)")
}
}
I have error that: suspension functions can be called only from coroutine body. BUT
When I have code like this, without returning String, then my delay works.. WHY?:
suspend fun showLoadingProgress() {
while (fileIsBeingLoaded())
{
delay(4000)
val percent = ((loadedBites.toDouble() / fileBites.toDouble())*100).toInt()
continuation.resume("$loadedBites/$fileBites ($percent%)")
}
}
How can I make delay and return a String?
suspendCancellableCoroutine is mainly used with callbacks to suspend a coroutine execution until the callback fires, for example:
suspend fun getUser(id: String): User = suspendCancellableCoroutine { continuation ->
Api.getUser(id) { user ->
continuation.resume(user)
}
continuation.invokeOnCancellation {
// clear some resources, cancel tasks, close streams etc.
}
}
delay doesn't work in suspendCancellableCoroutine block because it is not marked as suspend and therefore we can't call suspend function in it. suspendCancellableCoroutine function is defined like:
public suspend inline fun <T> suspendCancellableCoroutine(
crossinline block: (CancellableContinuation<T>) -> Unit
): T = ...
If it was defined something like this (please note block marked as suspend):
public suspend inline fun <T> suspendCancellableCoroutine(
crossinline block: suspend (CancellableContinuation<T>) -> Unit
): T = ...
then we would be able to call delay function in it.
I don't know why you use while loop, it seems it is redundant there. Or you use it incorrectly for the loading progress.
You don't have callbacks, so you can get rid of suspendCancellableCoroutine:
suspend fun getLoadingProgress(): String {
delay(4000)
val percent = ((loadedBites.toDouble() / fileBites.toDouble())*100).toInt()
return "$loadedBites/$fileBites ($percent%)"
}
suspend fun showLoadingProgress() {
while (fileIsBeingLoaded()) {
val progress = getLoadingProgress()
// use progress
}
}
Another approach is to use Flow to emit the loading progress. It will look something like the following using flow builder:
fun getLoadingProgress(): Flow<String> = flow {
while (fileIsBeingLoaded()) {
delay(4000)
val percent = ((loadedBites.toDouble() / fileBites.toDouble())*100).toInt()
emit("$loadedBites/$fileBites ($percent%)")
}
}
And collect values:
someCoroutineScope.launch {
getLoadingProgress().collect { progress ->
// use progress
}
}

Why is this extension function slower than non extension counterpart?

I was trying to write a parallel map extension function to do map operation over a List in parallel using coroutines.
However there is a significant overhead in my solution and I can't find out why.
This is my implementation of the pmap extension function:
fun <T, U> List<T>.pmap(scope: CoroutineScope = GlobalScope,
transform: suspend (T) -> U): List<U> {
return map { i -> scope.async { transform(i) } }.map { runBlocking { it.await() } }
}
However, when I do the exact same operation in a normal function, it takes up to extra 100ms (which is a lot).
I tried using inline but it had no effect.
I'm leaving here the full test I've done to demonstrate this behavior:
import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis
fun main() {
test()
}
fun <T, U> List<T>.pmap(scope: CoroutineScope = GlobalScope,
transform: suspend (T) -> U): List<U> {
return this.map { i -> scope.async { transform(i) } }.map { runBlocking { it.await() } }
}
fun test() {
val list = listOf<Long>(100,200,300)
val transform: suspend (Long) -> Long = { long: Long ->
delay(long)
long*2
}
val timeTakenPmap = measureTimeMillis {
list.pmap(GlobalScope) { transform(it) }
}
val manualpmap = measureTimeMillis {
list.map { GlobalScope.async { transform(it) } }
.map { runBlocking { it.await() } }
}
val timeTakenMap = measureTimeMillis {
list.map { runBlocking { transform(it) } }
}
println("pmapTime: $timeTakenPmap - mapTime: $timeTakenMap - manualpmap: $manualpmap")
}
It can be run in kotlin playground: https://pl.kotl.in/CIXVqezg3
In the playground it prints this result:
pmapTime: 411 - mapTime: 602 - manualpmap: 302
MapTime and manualPmap give reasonable results, only 2ms of time outside the delays. But pmapTime is way off. And the code between manualpmap and pmap looks exactly the same to me.
In my own machine it runs a little faster, pmap takes around 350ms.
Does anyone know why this happens?
First of all, manual benchmarks like this are usually of very little significance. There are many things that can be optimized away by the compiler or the JIT and any conclusion can be quite wrong. If you really want to compare things, you should instead use benchmarking libraries which take into account JVM warmup etc.
Now, the overhead you see (if you could confirm there was an actual overhead) might be caused by the fact that your higher-order extension is not marked inline, so instances of the lambda you pass need to be created - but as #Tenfour04 noted there are many other possible reasons: thread pool lazy initialization, significance of the list size, etc.
That being said, this is really not an appropriate way to write parallel map, for several reasons:
GlobalScope is a pretty bad default in general, and should be used in very specific situations only. But don't worry about it because of the next point.
You don't need an externally provided CoroutineScope if the coroutines you launch do not outlive your method. Instead, use coroutineScope { ... } and make your function suspend, and the caller will choose the context if they need to
map { it.await() } is inefficient in case of errors: if the last element's transformation immediately fails, map will wait for all previous elements to finish before failing. You should prefer awaitAll which takes care of this.
runBlocking should be avoided in coroutines (blocking threads in general, especially when you don't control which thread you're blocking), so using it in deep library-like functions like this is dangerous, because it will likely be used in coroutines at some point.
Applying those points gives:
suspend inline fun <T, U> List<T>.pmap(transform: suspend (T) -> U): List<U> {
return coroutineScope {
map { async { transform(it) } }.awaitAll()
}
}

Using runBlocking in Kotlin Coroutines when targeting JavaScript?

Is there a way to write the Kotlin code below so that it compiles and works the same way on the JVM and in JavaScript?
fun <A: Any> request(request: Any): A = runBlocking {
suspendCoroutine<A> { cont ->
val subscriber = { response: A ->
cont.resume(response)
}
sendAsync(request, subscriber)
}
}
fun <Q : Any, A : Any> sendAsync(request: Q, handler: (A) -> Unit) {
// request is sent to a remote service,
// when the result is available it is passed to handler(... /* result */)
}
The code compiles and works fine when compiled to target the JVM.
A compilation error is emitted when targeting JavaScript due to non-existent function runBlocking
Your main problem is that you aren't asking for the thing you actually need. The code you wrote starts a coroutine, suspends it, then blocks until it's done. This is exactly equivalent to having no coroutines at all and just making a blocking network request, which is something you can't possibly expect JavaScript to allow you.
What you actually have to do is step back to the call site of request() and wrap it in a launch:
GlobalScope.launch(Dispatchers.Default) {
val result: A = request(...)
// work with the result
}
With this in place you can rewrite your request function to
suspend fun <A: Any> request(request: Any): A = suspendCancellableCoroutine {
sendAsync(request, it::resume)
}

Kotlin Process Collection In Parallel?

I have a collection of objects, which I need to perform some transformation on. Currently I am using:
var myObjects: List<MyObject> = getMyObjects()
myObjects.forEach{ myObj ->
someMethod(myObj)
}
It works fine, but I was hoping to speed it up by running someMethod() in parallel, instead of waiting for each object to finish, before starting on the next one.
Is there any way to do this in Kotlin? Maybe with doAsyncTask or something?
I know when this was asked over a year ago it was not possible, but now that Kotlin has coroutines like doAsyncTask I am curious if any of the coroutines can help
Yes, this can be done using coroutines. The following function applies an operation in parallel on all elements of a collection:
fun <A>Collection<A>.forEachParallel(f: suspend (A) -> Unit): Unit = runBlocking {
map { async(CommonPool) { f(it) } }.forEach { it.await() }
}
While the definition itself is a little cryptic, you can then easily apply it as you would expect:
myObjects.forEachParallel { myObj ->
someMethod(myObj)
}
Parallel map can be implemented in a similar way, see https://stackoverflow.com/a/45794062/1104870.
Java Stream is simple to use in Kotlin:
tasks.stream().parallel().forEach { computeNotSuspend(it) }
If you are using Android however, you cannot use Java 8 if you want an app compatible with an API lower than 24.
You can also use coroutines as you suggested. But it's not really part of the language as of now (August 2017) and you need to install an external library. There is very good guide with examples.
runBlocking<Unit> {
val deferreds = tasks.map { async(CommonPool) { compute(it) } }
deferreds.forEach { it.await() }
}
Note that coroutines are implemented with non-blocking multi-threading, which mean they can be faster than traditional multi-threading. I have code below benchmarking the Stream parallel versus coroutine and in that case the coroutine approach is 7 times faster on my machine. However you have to do some work yourself to make sure your code is "suspending" (non-locking) which can be quite tricky. In my example I'm just calling delay which is a suspend function provided by the library. Non-blocking multi-threading is not always faster than traditional multi-threading. It can be faster if you have many threads doing nothing but waiting on IO, which is kind of what my benchmark is doing.
My benchmarking code:
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.runBlocking
import java.util.*
import kotlin.system.measureNanoTime
import kotlin.system.measureTimeMillis
class SomeTask() {
val durationMS = random.nextInt(1000).toLong()
companion object {
val random = Random()
}
}
suspend fun compute(task: SomeTask): Unit {
delay(task.durationMS)
//println("done ${task.durationMS}")
return
}
fun computeNotSuspend(task: SomeTask): Unit {
Thread.sleep(task.durationMS)
//println("done ${task.durationMS}")
return
}
fun main(args: Array<String>) {
val n = 100
val tasks = List(n) { SomeTask() }
val timeCoroutine = measureNanoTime {
runBlocking<Unit> {
val deferreds = tasks.map { async(CommonPool) { compute(it) } }
deferreds.forEach { it.await() }
}
}
println("Coroutine ${timeCoroutine / 1_000_000} ms")
val timePar = measureNanoTime {
tasks.stream().parallel().forEach { computeNotSuspend(it) }
}
println("Stream parallel ${timePar / 1_000_000} ms")
}
Output on my 4 cores computer:
Coroutine: 1037 ms
Stream parallel: 7150 ms
If you uncomment out the println in the two compute functions you will see that in the non-blocking coroutine code the tasks are processed in the right order, but not with Streams.
You can use RxJava to solve this.
List<MyObjects> items = getList()
Observable.from(items).flatMap(object : Func1<MyObjects, Observable<String>>() {
fun call(item: MyObjects): Observable<String> {
return someMethod(item)
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : Subscriber<String>() {
fun onCompleted() {
}
fun onError(e: Throwable) {
}
fun onNext(s: String) {
// do on output of each string
}
})
By subscribing on Schedulers.io(), some method is scheduled on background thread.
To process items of a collection in parallel you can use Kotlin Coroutines. For example the following extension function processes items in parallel and waits for them to be processed:
suspend fun <T, R> Iterable<T>.processInParallel(
dispatcher: CoroutineDispatcher = Dispatchers.IO,
processBlock: suspend (v: T) -> R,
): List<R> = coroutineScope { // or supervisorScope
map {
async(dispatcher) { processBlock(it) }
}.awaitAll()
}
This is suspend extension function on Iterable<T> type, which does a parallel processing of items and returns some result of processing each item. By default it uses Dispatchers.IO dispatcher to offload blocking tasks to a shared pool of threads. Must be called from a coroutine (including a coroutine with Dispatchers.Main dispatcher) or another suspend function.
Example of calling from a coroutine:
val myObjects: List<MyObject> = getMyObjects()
someCoroutineScope.launch {
val results = myObjects.processInParallel {
someMethod(it)
}
// use processing results
}
where someCoroutineScope is an instance of CoroutineScope.
Or if you want to just launch and forget you can use this function:
fun <T> CoroutineScope.processInParallelAndForget(
iterable: Iterable<T>,
dispatcher: CoroutineDispatcher = Dispatchers.IO,
processBlock: suspend (v: T) -> Unit
) = iterable.forEach {
launch(dispatcher) { processBlock(it) }
}
This is an extension function on CoroutineScope, which doesn't return any result. It also uses Dispatchers.IO dispatcher by default. Can be called using CoroutineScope or from another coroutine.
Calling example:
someoroutineScope.processInParallelAndForget(myObjects) {
someMethod(it)
}
// OR from another coroutine:
someCoroutineScope.launch {
processInParallelAndForget(myObjects) {
someMethod(it)
}
}
where someCoroutineScope is an instance of CoroutineScope.

Extend Mockito verify for Kotlin not working (in a "kotlin way")

I want to extend verify to allow checking multiple commands over the same mocked object but it is not working, it compiles but on run it dont run each command over the same.
Just want to avoid writing more things like:
Mockito.verify(mockedView).initViews()
Mockito.verify(mockedView).setImage(user.photoUrl)
and write more like:
Mockito.verify(mockedView){
initViews()
setImage(user.photoUrl)
}
First try:
#Test
fun onCreate_loadLoginInfo() {
val user = MockUser.user()
presenter.onCreate(mockedView, user)
Mockito.myVerify(mockedView) {
initViews()
setImage(user.photoUrl)
setName(user.name)
setEmail(user.email)
}
}
class Mockito {
companion object
}
fun <T> Mockito.Companion.myVerify(obj: T, func: T.() -> Unit) {
org.mockito.Mockito.verify(obj).func()
}
Second try:
#Test
fun onCreate_loadLoginInfo() {
val user = MockUser.user()
presenter.onCreate(mockedView, user)
Mockito.myVerify(mockedView) {
it.initViews()
it.setImage(user.photoUrl)
it.setName(user.name)
it.setEmail(user.email)
}
}
class Mockito {
companion object
}
fun <T> Mockito.Companion.myVerify(obj: T, func: (T) -> Unit) {
val mock = org.mockito.Mockito.verify(obj)
func(mock)
}
But those are not working, all the tests pass even if I dont call the methods in the presenter, How can I do this?
I had the same problems and wrote Facade around Mockito.
My library allow to verify few calls around one mock object:
val list = mock(MutableList::class)
list.add("String 1")
list.add("String 2")
list.size()
verify(list) {
times(2).add(anyString())
times(1).size()
}
Please look to the readme, maybe it can help you
Correct me if I'm wrong. You want to avoid multiple verify() calls in your test.
#Test fun onCreate_loadLoginInfo() {
// ...
verify(mockedView).initViews()
verify(mockedView).setImage(user.photoUrl)
verify(mockedView).setName(user.name)
verify(mockedView).setEmail(user.email)
}
I modified your second approach little bit:
#Test fun onCreate_loadLoginInfo() {
// ...
verifyAll(mockedView) {
it().initViews()
it().setImage(user.photoUrl)
it().setName(user.name)
it().setEmail(user.email)
}
}
fun <T> verifyAll(mock: T, func: (() -> T) -> Unit) {
func { Mockito.verify(mock) }
}
As you can see now we are passing functional argument to func() and need to use it appropriately (use it as function, not as object).
You should do it like that. verify must be called before each mock method invocation.