Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing Retrofit with coroutines

How do you properly test an Http request/response with Retrofit and Coroutines?

I'm using the latest Retrofit version: 2.6.0 which has the support on the suspend functions.

Edit

I'm also providing some code for easy implementation:

ApiInterface

@GET
suspend fun getCountriesListAsync(@Url url: String): Response<ArrayList<Country>>

Some Repository

suspend fun  makeCountryApiCallAsync(): Response<ArrayList<Country>> =
    treasureApi.getCountriesListAsync(COUNTRIES_API_URL)

Implementation on the ViewModel:

private suspend fun makeCountriesApiCall() {
        withContext(Dispatchers.Main) {
            switchProgressBarOn()
        }
        try {
            val countriesListResponse = setupRepository.makeCountryApiCallAsync()
            if (countriesListResponse.isSuccessful) {
                withContext(Dispatchers.Main) {
                    switchProgressOff()
                    countriesList.value = countriesListResponse.body()
                }
            } else {
                withContext(Dispatchers.Main) {
                    showErrorLayout()
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
            withContext(Dispatchers.Main) {
                showErrorLayout()
            }
        }
    }
like image 743
coroutineDispatcher Avatar asked Jun 11 '19 12:06

coroutineDispatcher


1 Answers

I suppose you are asking how to test coroutines?

You should test wherever your logic is. As I see your Repository does not contain any logic beside Api call. If this is the case you can't test against live data because it's inconsistent but you can use WireMock or Mockito to mock some result and try to make it goes through your logics.

There's coroutine test support that you can have a look at it.

Here's an example

Essential for coroutine test (you can do without but with this is much easier)

testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.0-M1'

Optional that I use to mock my call results

testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0'

With what you gave as example

@ExperimentalCoroutinesApi
class ViewModelTest {
    private val testDispatcher = TestCoroutineDispatcher()
    private val testScope = TestCoroutineScope(testDispatcher)

    @Before
    fun before() {
        Dispatchers.setMain(testDispatcher)
    }

    @After
    fun after() {
        Dispatchers.resetMain()
        testScope.cleanupTestCoroutines()
    }

    @Test
    fun testYourFunc() = testScope.runBlockingTest {
        val mockRepo = mock<MyRepository> {
            onBlocking { makeCountryApiCallAsync("") } doReturn Response.success(listOf())
        }

        val viewModel = TheViewModel(mockRepo)

        val result = viewModel.makeCountriesApiCall() // Or however you can retrieve actual changes the repo made to viewmodel

        // assert your case
    }
}
like image 135
Anh Hoang Avatar answered Sep 18 '22 06:09

Anh Hoang