Skip to content

Checking if a method in the API interface is called

Devrath edited this page Jun 26, 2021 · 3 revisions

Scenario of the test

  • Consider we need to validate a service, This service returns a list of images
  • We need to check that if the number of images returned is 30
  • Here some of the challenges we face are that How to mock the service and going further how to check using the service the API returns a certain type of API response and further there are 30 images present in it.

ImageListProvider.kt

class ImageListProvider(private val api: SpacingOutApi) {
  // Requests 30 images from the API
  private val numImages = 30
  suspend fun buildImageList(): List<ApodImage> = coroutineScope {

    val today = LocalDate.now()

    val deferredImages = mutableListOf<Deferred<ApodImage>>()

    for (i in 0 until numImages) {
      val day = today.minusDays(i.toLong())
      val image = async(Dispatchers.IO) {
        api.getImage(day.format(DateTimeFormatter.ISO_DATE)).execute().body()!!
      }
      deferredImages.add(image)
    }

    deferredImages.map { it.await() }
  }
}

SpacingOutApi.kt

interface SpacingOutApi {
  companion object {
    fun create(): SpacingOutApi {
      val client = OkHttpClient.Builder().addInterceptor { chain ->
        val url = chain.request().url().newBuilder().addQueryParameter("api_key", "IaZbUHgLeD5G3evuRL0ZDlaAXP9bIVPU919Gt9CF").build()
        val request = chain.request().newBuilder().url(url).build()
        chain.proceed(request)
      }.build()
      return Retrofit.Builder()
          .addConverterFactory(MoshiConverterFactory.create())
          .baseUrl("https://api.nasa.gov")
          .client(client)
          .build()
          .create(SpacingOutApi::class.java)
    }
  }

  @GET("/planetary/apod")
  fun getImage(@Query("date") date: String): Call<ApodImage>

  @GET("/planetary/earth/imagery/")
  suspend fun getEarthImagery(@Query("lon") longitude: Float, @Query("lat") latitude: Float): EarthImage
}

Test

/**
     * RunBlockingTest is used to ensure that the all the coroutines that are involved
     * in the test are completed before the test finishes
     **/
    @Test
    fun `a list of 30 images is returned from a api service`() = runBlocking {

        // When
        val expectedOutput = 30

        // Since this test needs a api as input - We will use MockK to mock the interface
        val spacingApi = mockk<SpacingOutApi>()
        // Now once the mock is available, we want to ensure its returning the real value that you want it to return when invoked in real code
        // Here in our case when we call the the function the in interface and get the object the function returns
        // Here only the type matters more than the data in it
        // ANSWERS: This will return whatever the block return whenever the block is executed.
        every {
            spacingApi.getImage(date = any())
        } answers {
            Calls.response(
                ApodImage(
                    date = "01-01-2020",
                    explanation = "Image is of some response",
                    hdurl = "www.testurl.com",
                    title = "test",
                    url = "www.testurl.com",
                    media_type = "video"
                )
            )
        }

        // -----------------------------------------> SUT
        val provider = ImageListProvider(api = spacingApi)
        val images = provider.buildImageList()

        // Then
        assertEquals(expectedOutput,images.size)

}
Clone this wiki locally