Retrofit – probably the most popular networking client in Android development. Basically it allows to create HTTP client in an interface – you just add annotation with HTTP method, relative or absolute path and proper request is constructed. Retrofit does not generate code in compile time – it creates implementations in runtime.
import okhttp3.ResponseBodyimport retrofit2.Callimport retrofit2.http.GETimport retrofit2.http.Queryinterface RemoteApi {    @GET("http://some.host/api/data")    fun searchByPhrase(@Query("search") searchPhrase: String): Call<ResponseBody>}The method invocation:
remoteApi.searchByPhrase("asd")Will create GET request to:
http://some.host/api/data?search=asd Now we will try to come up with a way to check if proper requests are constructed.
Basic test case
We will consider the following Retrofit interface with given configuration:
fun remoteApi(baseUrl: String): RemoteApi {    return Retrofit.Builder()            .client(OkHttpClient())            .baseUrl(baseUrl)            .build()            .create(RemoteApi::class.java)}interface RemoteApi {    @GET("/api/data")    fun searchByPhrase(@Query("search") searchPhrase: String): Call<ResponseBody>}How could we actually perform some assertions on that? Was actually GET method used? Was search phrase included in query?
While we are using retrofit2.Call<T> we have Call.request() method available directly and we can perform assertions on Request object:
import okhttp3.ResponseBodyimport org.junit.jupiter.api.Testimport retrofit2.Callimport retrofit2.Retrofitimport retrofit2.http.GETimport retrofit2.http.Queryimport strikt.api.expectThatclass RetrofitTest {    @Test    fun `it should GET with query`() {        val remoteApi = remoteApi(baseUrl = "http://some.api")        val givenSearchQuery = "given search phrase"        val call: Call<ResponseBody> = remoteApi.searchByPhrase(givenSearchQuery)        expectThat(call.request()) {            assertThat("is GET method") {                it.method() == "GET"            }            assertThat("has given search query") {                it.url().queryParameterValues("search") == listOf(givenSearchQuery)            }        }    }}Test case with non-standard call adapter factory
In my opinion the power of Retrofit comes from call and converter adapter factories – instead of relying on built-in types (such as retrofit2.Call) you can add RxJava call adapter and framework would find a way to convert Call<T> into rx Single<T>. Unfortunately we won’t have direct access to Call.request() method then!
fun remoteApi(baseUrl: HttpUrl): RemoteApi {    return Retrofit.Builder()            .client(OkHttpClient())            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())            .baseUrl(baseUrl)            .build()            .create(RemoteApi::class.java)}interface RemoteApi {    @GET("/api/data")    fun searchByPhrase(@Query("search") searchPhrase: String): Single<ResponseBody>}To handle this case and have request to perform assertions on I came up with the following approach:
- Use OkHttp MockWebServer to record incoming requests
- Wrap system under test invocation with RemoteApi as lambda parameter and RecordedRequest as return type
- Perform assertions on RecordedRequest
import io.reactivex.Singleimport okhttp3.HttpUrlimport okhttp3.OkHttpClientimport okhttp3.ResponseBodyimport okhttp3.mockwebserver.MockResponseimport okhttp3.mockwebserver.MockWebServerimport okhttp3.mockwebserver.RecordedRequestimport org.junit.jupiter.api.Testimport retrofit2.Retrofitimport retrofit2.adapter.rxjava2.RxJava2CallAdapterFactoryimport retrofit2.http.GETimport retrofit2.http.Queryimport strikt.api.expectThatclass RetrofitTest {    @Test    fun `it should GET with query`() {        val givenSearchQuery = "given search phrase"        val request: RecordedRequest = takeMockRequest {            searchByPhrase(givenSearchQuery)                    .subscribe()        }        expectThat(request) {            assertThat("is GET method") {                it.method == "GET"            }            assertThat("has given search query") {                it.requestUrl.queryParameterValues("search") == listOf(givenSearchQuery)            }        }    }    private fun takeMockRequest(sut: RemoteApi.() -> Unit): RecordedRequest {        return MockWebServer()                .use {                    it.enqueue(MockResponse())                    it.start()                    val url = it.url("/")                    sut(remoteApi(url))                    it.takeRequest()                }    }}I highly encourage you to explore more methods and properties available in RecorderRequest – you may find there more things that may be useful in your test.
Now we have a way to perform assertions on request that was recorded in MockWebServer – with this approach we can design even more complex integration test suites.
Links:

