
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 if 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 not 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 } }