is it possible to mock a retrofit Deferred<Response<T>> in Kotlin?

Viewed 2358

I am wondering if I can do this mock

Deferred<Response<Void>>

I am new at Kotlin and at mock but I have unsuccessfully made it.

So far I have done this:

I have a client interface:

class MyClient {

@POST("/names")
    @FormUrlEncoded
    fun names(
        @Field("username") username: String
    ): Deferred<Response<Void>>

}

On my tests:

lateinit var myClient : MyClient

@Before
fun setUp() {
    client = mockk<MyClient>(relaxed = true)
    val response = mockk<Response<Void>>(relaxed = true)
    every { myClient.names(username) }.returns(mockedResponse)

}

I've found many posts about Response (like this Handle empty response with retrofit and rxjava 2.x ) but not really sure how to handle the "Deferred"

or some about deferred (cannot implement the answer of this. async cannot be resolved in my project

When using kotlin coroutines, how do I unit test a function that calls a suspend function?

Is this possible? what am I missing?

The service I am testing:

class MyService {

    fun doSomething(username: String) {
       myClient().names(username).await()
    }
}
2 Answers

You are trying to return a Response<Void> where a Deferred<Response<Void>> would be required.

The deferred is basically the result of a job that may have not yet completed. When the deferred isCompleted, the result is ready. In your case you most likely want to have a Deferred that is already completed with the response.

To create a Deferred that is already completed with some value you can use the CompletableDeferred(value: T) function.

Here is a full example of how to return a completed deferred from a client Mock.

interface GithubClient {
    @GET("users/{username}/repos")
    fun listRepos(@Path("username") username: String): Deferred<Response<Void>>
}

class RepoService(val client: GithubClient) {
    suspend fun doSomething(username: String): Response<Void> {
        return client.listRepos(username).await()
    }
}

class GithubClientTests {

    private lateinit var client: GithubClient

    @BeforeTest
    fun setUp() {
        // setup the mocked client
        client = mockk<GithubClient>(relaxed = true)
        val mockResponse = mockk<Response<Void>>(relaxed = true)
        every { client.listRepos(any()) } returns CompletableDeferred(mockResponse)
    }

    @Test
    fun testMockClient() = runBlocking<Unit> {
        // create the service with the mocked client
        val service = RepoService(client)
        val response = service.doSomething("octocat")

        // response is not successful, because we didn't specify any behavior for the mocked response
        assertFalse { response.isSuccessful }
    }
}

I would say that you're missing an await:

@Before
fun setUp() {
    client = mockk<MyClient>(relaxed = true)
    val response = mockk<Response<Void>>(relaxed = true)
    every { myClient.names(username).await() /* this would return an response instead of deferred */ }.returns(mockedResponse)
}

Let me know if this helps!

Related