article banner

Exercise: Testing mapAsync

Your task is to test the mapAsync function, which maps elements of a collection asynchronously.

suspend fun <T, R> Iterable<T>.mapAsync( transformation: suspend (T) -> R ): List<R> = coroutineScope { map { async { transformation(it) } } .awaitAll() }

You should test the following cases:

  • should behave like a regular map for lists and sets
  • should map asynchronously
  • should keep elements' order
  • should support context propagation
  • should support cancellation
  • should immediately throw exception it it occurs in transformation

To verify that your unit tests are correct, check if the following implementations fail the relevant tests:

// does not map asynchronously suspend fun <T, R> Iterable<T>.mapAsync( transformation: suspend (T) -> R ): List<R> = map { transformation(it) } // does not keep elements order suspend fun <T, R> Iterable<T>.mapAsync( transformation: suspend (T) -> R ): List<R> = this .asFlow() .flatMapMerge { flow { emit(transformation(it)) } } .toList() // does support context propagation or cancellation suspend fun <T, R> Iterable<T>.mapAsync( transformation: suspend (T) -> R ): List<R> = coroutineScope { map { GlobalScope.async { transformation(it) } } .awaitAll() } // does not immediately throw an exception from transformation // (waits until the failing coroutine is awaited) suspend fun <T, R> Iterable<T>.mapAsync( transformation: suspend (T) -> R ): List<R> = supervisorScope { map { async { transformation(it) } } .map { it.await() } }

This problem can either be solved in the below playground or you can clone kotlin-exercises project and solve it locally. In the project, you can find code template for this exercise in coroutines/test/MapAsyncTest.kt. You can find there starting code.

Once you are done with the exercise, you can check your solution here.

Playground

import kotlinx.coroutines.* import kotlinx.coroutines.test.currentTime import kotlinx.coroutines.test.runTest import org.junit.Test import kotlin.coroutines.CoroutineContext import kotlin.test.assertEquals import kotlin.test.assertTrue suspend fun <T, R> Iterable<T>.mapAsync( transformation: suspend (T) -> R ): List<R> = coroutineScope { map { async { transformation(it) } } .awaitAll() } class MapAsyncTest { @Test fun `should behave like a regular map`() = runTest { // TODO } @Test fun `should map async`() = runTest { // TODO } @Test fun `should keep elements order`() = runTest { // TODO } @Test fun `should support context propagation`() = runTest { // TODO } @Test fun `should support cancellation`() = runTest { // TODO } @Test fun `should propagate exceptions`() = runTest { // TODO } }