Testing Suspending Functions
Learn how to test suspending functions and get an overview of their time dependency.
We'll cover the following...
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
assertEqualsare 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)Note: Our method ...