Skip to content

Async Testing ‐ When to use blocking tests

Devrath edited this page Oct 25, 2023 · 3 revisions

github-header-image (2)

Scenario

  • Sometimes you have a scenario when you have a suspend function in the production code.
  • This suspend function has two parallel co-routines that spawn independently.
  • The enclosing suspend function waits for the two of them to complete, Only then the result is returned.

Run Blocking Way

  • Here we can use the runBlockingWay

Code

UserApi.kt

interface UserApi {

    @GET("/user")
    suspend fun getUser(@Path("id") id: String): User?

    @GET("/posts")
    suspend fun getPosts(@Query("userId") id: String): List<Post>
}

UserApiFake.kt

class UserApiFake : UserApi {

    var users = (1..10).map {
        User(id = it.toString(), username = "User$it")
    }

    var posts = (1..10).map{
        Post(
            id = it.toString(),
            userId = it.toString(),
            title = "Test title$it",
            body = "Test body$it"
        )
    }

    override suspend fun getUser(id: String): User? {
        // Return the matching user Id
        return users.find { it.id == id }
    }

    override suspend fun getPosts(id: String): List<Post> {
        // GEt all the posts that belong to particular user
        return posts.filter { it.id == id }
    }
}

UserRepositoryImpl.kt

class UserRepositoryImpl(
    private val api: UserApi
): UserRepository {

    override suspend fun getProfile(userId: String): Result<Profile> {
        return coroutineScope {
            val userResult = async {
                try {
                    Result.success(api.getUser(userId))
                } catch(e: HttpException) {
                    Result.failure(e)
                } catch(e: IOException) {
                    Result.failure(e)
                }
            }
            val postsResult = async {
                try {
                    Result.success(api.getPosts(userId))
                } catch(e: HttpException) {
                    Result.failure(e)
                } catch(e: IOException) {
                    Result.failure(e)
                }
            }

            val fetchedUser = userResult.await().getOrNull()
            if(fetchedUser != null) {
                Result.success(
                    Profile(
                        user = fetchedUser,
                        posts = postsResult.await().getOrNull() ?: emptyList()
                    )
                )
            } else {
                Result.failure(
                    userResult.await().exceptionOrNull() ?: Exception("Unknown error")
                )
            }

        }
    }
}

Test

UserRepositoryImplTest.kt

class UserRepositoryImplTest {

    private lateinit var repository : UserRepositoryImpl
    private lateinit var api : UserApiFake
    @BeforeEach
    fun setUp() {
        api = UserApiFake()
        repository = UserRepositoryImpl(api = UserApiFake())
    }


    @Test
    fun `Test getting profile`() = runBlocking {
        val profileResult = repository.getProfile("1")

        assertThat(profileResult.isSuccess).isTrue()
        assertThat(profileResult.getOrThrow().user.id).isEqualTo("1")

        val expectedPosts = api.posts.filter { it.userId == "1" }
        val resultOfImpl = repository.getProfile("1")

        assertThat(resultOfImpl.getOrThrow().user.id).isEqualTo(expectedPosts[0].userId)

    }

}