...

/

Testing Suspending Functions

Testing Suspending Functions

Learn how to test suspending functions and get an overview of their time dependency.

Testing suspending functions in most cases is not different from testing normal functions. Take a look at the fetchUserData below from FetchUserUseCase. Checking whether it shows data, as expected, can be easily achieved thanks to a few fakes (or mocks) and simple assertions.

A fake class implements an interface, but contains fixed data and no logic. They help mimic a concrete behavior for testing.

Mocks are universal simulated objects that mimic the behavior of real objects in controlled ways. We generally create them using libraries, like MockK, which support mocking suspending functions. In the examples below, we have decided to use fakes to avoid using an external library.

Note: The example below will only throw an exception when both values passed to assertEquals are different.

package kotlinx.coroutines.app
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.runBlocking
import org.junit.Test
import kotlin.test.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi

class FetchUserUseCase(
    private val repo: UserDataRepository,
) {

    suspend fun fetchUserData(): User = coroutineScope {
        val name = async { repo.getName() }
        val friends = async { repo.getFriends() }
        val profile = async { repo.getProfile() }
        User(
            name = name.await(),
            friends = friends.await(),
            profile = profile.await()
        )
    }
}

@ExperimentalCoroutinesApi
class FetchUserDataTest {

    @Test
    fun `should construct user`() = runBlocking {
        // given
        val repo = FakeUserDataRepository()
        val useCase = FetchUserUseCase(repo)

        // when
        val result = useCase.fetchUserData()

        // then
        val expectedUser = User(
            name = "Ben",
            friends = listOf(Friend("some-friend-id-1")),
            profile = Profile("Example description")
        )
        println(expectedUser)
        println(result)
        assertEquals(expectedUser, result)
    }

    class FakeUserDataRepository : UserDataRepository {
        override suspend fun getName(): String = "Ben"

        override suspend fun getFriends(): List<Friend> =
            listOf(Friend("some-friend-id-1"))

        override suspend fun getProfile(): Profile =
            Profile("Example description")
    }
}

interface UserDataRepository {
    suspend fun getName(): String
    suspend fun getFriends(): List<Friend>
    suspend fun getProfile(): Profile
}

data class User(
    val name: String,
    val friends: List<Friend>,
    val profile: Profile
)

data class Friend(val id: String)
data class Profile(val description: String)
Using fakes for testing

Note: Our method ...