Skip to content

How to mock classes that are not built using the dependency injection principles

Devrath edited this page Jun 26, 2021 · 2 revisions

Use case - 1

  • There are many cases where the classes are not built using the dependency injection and we need to test those classes
  • We can use mockK object for this

Testing the service class

SpacingOutApi

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/apod")
  suspend fun getImage(@Query("date") date: String): ApodImage

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

Test class

 @Test
    fun `a image is sent through the view after the co-ordinates are entered`() {

        /*** ****************** BLOCK TO MOCK THE API ****************** ***/
        val mockApi = mockk<SpacingOutApi>()
        // Using the mockK object you can mock the kotlin objects and return what you want
        mockkObject(SpacingOutApi)
        // After above step you can check using mocking syntax you can return what you want
        // Now in below line whenever we mock the api, we can ensure we return the mockApi that is needed
        every { SpacingOutApi.create() } returns mockApi
        /*** ****************** BLOCK TO MOCK THE API ****************** ***/

        coEvery { mockApi.getEarthImagery(any(),any()) } answers {
            EarthImage("TestUrl")
        }

        val  viewModel = LookupViewModel()
        viewModel.latLongInput(latitude = 10f,longitude = 10f)

        assertEquals("TestUrl",viewModel.imageLiveData.value)

    }

Use case - 2 -> Using a capturing tool

  • There is a scenario where it accepts a certain set of input values.
  • Now again it adds certain more values to it.
  • Then calls the third-party service using old values appended with new values.
 @Test
    fun `new analytics events are added before the events are sent`() {

        mockkObject(ThirdPartyAnalyticsProvider)

        // Lets use the combination of slot and capture

        // slot is a capturing tool used to capture the arguments
        val slot = slot<Map<String,String>>()

        every { ThirdPartyAnalyticsProvider.logEvent(any(),capture(slot)) } just Runs

        SpacingAnalytics().logEvent("Test event", mapOf("attribute" to "value"))

        // Now you expect to call the third party analytics with the value you supplied along with other attributes
        val expected = mapOf(
                "attribute" to "value",
                "client_type" to "Android",
                "version" to "1"
        )

        // List of attributes passed to the analytics was captured in the slot previously supplied earlier
        assertEquals(expected,slot.captured)

    }
Clone this wiki locally