Testing Retrofit API calls in Android

Testing Retrofit API calls in Android

Retrofit is a type-safe HTTP-Client that you are using in your Android project to consume the API responses you want.

Why need to test this?

  1. Your API development is independent of your Android project and it might have some structural changes over time. So, to ensure everything works fine after the changes, you can test your API calls.

  2. If your APIs are currently not ready, then instead of waiting for it just get the request/response json structure and implement it in your project to maintain the development flow going.

  3. You can test how your app is working according to the different responses.

How to do?

  1. Using MockWebServer.

Pre-requisites:

  1. Should have Retrofit Interface ready beforehand(I’ll not create it here).

  2. You should have your Request/Response object structures.

  3. Understanding of basics of Unit testing. Check VM testing.

  4. Having good app arch like MVVM(or other).

Implementation:

  1. Add the following dependency in your app level build.gradle:
testImplementation("com.squareup.okhttp3:mockwebserver:4.11.0")

2. Your API interface might look like:

interface FeedbackApi {

    @GET("feedback/user/get/?")
    suspend fun getFeedbackQuestions(
        @Query("uid") userId: String,
        @Query("fid") featureId: String
    ): FeedbackQuesDTO
}

Here, instead of directly getting the DTO object as a function call response, you might be haivng a Retrofit wrapper of Response. Don’t worry I’ll also show you how to proceed with that.

3. Now, create a corresponding Test class for this Interface.

Tip: For shortcut, change this interface to a class and then press ALT+INSERT -> Test -> Generate the test class, and then revert it back to interface.

4. Now, you’ll have a Test class, there we need the instance of our fake server(which will take the call and send the response) and our Interface.

class FeedbackApiTest {
    private lateinit var server: MockWebServer//Fake server from square lib
    private lateinit var api: FeedbackApi

    @BeforeEach//Using JUnit5
    fun beforeEach() {
        server = MockWebServer()
        api = Retrofit.Builder()
            .baseUrl(server.url("/"))//Pass any base url like this
            .addConverterFactory(GsonConverterFactory.create())
            .build().create(FeedbackApi::class.java)
    }
}

5. Also, don’t forget to shut down the server to avoid memory leaks.

    @AfterEach
    fun afterEach() {
        server.shutdown()
    }

6. Create your first test, refer to comments for explanation:

    @Test
    fun `getFeedbackQuestions, returns Success`() = runTest {
        val dto = FeedbackQuesDTO()//The object I want back as response
        private val gson: Gson = GsonBuilder().create()
        val json = gson.toJson(dto)!!//Conver the object into json string using GSON
        val res = MockResponse()//Make a fake response for our server call
        res.setBody(json)//set the body of the fake response as the json string you are expecting as a response
        server.enqueue(res)//add it in the server response queue

        val data = api.getFeedbackQuestions("", "")//make the call to our fake server(as we are using fake base url)
        server.takeRequest()//let the server take the request

        assertEquals(data, dto)//the data you are getting as the call response should be same
    }

7. If you’re using Retrofit ResponseState wrapper for taking the api responses.

@Test
    fun `getFeedbackQuestions, returns Success`() = runTest {
        val res = MockResponse()//Make a fake response for our server call
        res.setBody("[]")//set the body of the fake response as the json string you are expecting as a response
        server.enqueue(res)//add it in the server response queue

        val data = api.getFeedbackQuestions("", "")//make the call to our fake server(as we are using fake base url)
        server.takeRequest()//let the server take the request

        assertEquals(data.body()!!.isEmpty(),true)//the data you are getting as the call response should be same
    }

You can also set the response code using the setResponseCode method of your MockResponse object.

8. Another test case for checking HTTP errors.

    fun `getFeedbackQuestions, returns Error`() = runTest {
        //First step is to make the server ready with a response
        val res = MockResponse()
        res.setResponseCode(404)
        server.enqueue(res)

        //Second step is to create a call
        val repo = FeedbackRepoImpl(api, mockk())
        val data = repo.getFeedbackQuestions("", "")

        //Third step is to tell our server to accept the call created
        server.takeRequest()

        assert(data is MyResponseState.Error)//Our repo shows error as the response code was 400.
    }

This is jut for attraction purpose

Here, in this test case we are depending on our RepoImpl to send us the MyResponseState.Error when we hit HTTP 4**.

This is not a good practice because the if your repo is not working properly then this test will also fail.

Let me know any better way to test failure cases when we are not using the Response wrapper provided by retrofit.

But, if you’re using the Response wrapper, then it’s not a problem for you, just check the code you are getting after making the call is same.

9. If you are having any Request objects also then either create them here manually or just mock them using library like mockK.

See mockK in action here.

The above thing may seem very obvious to have. Like the things you are passing in your fake server is what you are getting after making the fake call.

But, this is what we can testing. This thing will definitely help you verify the flow when suppose you make any change to your function param or response or maybe request object.

I hope you found this helpful. If yes, then do FOLLOW ‘Sagar Malhotra’ for more Android-related content.