'Mocking internal function call in Kotlin

I am a complete beginner in terms of Kotlin and I am finding some issues while trying to test out a Ktor based application.

I have a file in my endpoints package localized at org.example.endpoints.hello

this file contains a fun Application.hello that implements an endpoint for my application.

This endpoint acts as a wrapper for another API, so inside that same file I have a

fun callOtherAPI(): ResponseContainer {
  // networking stuff
  return ResponseContainer(message: "some stuff")
}

This function gets called inside the Application's function routing implementation as such:

    routing {
        get("/hello") {
            call.respond(callOtherAPI())
        }
    }

Now to the issue:

My test currently looks like this:

    @Test
    fun testHello() = testApplication {
        application {
            hello()
        }
        mockkStatic(::callOtherAPI)
        every { callOtherAPI() } returns ResponseContainer("hello")
        print(callOtherAPI()) // This actually returns the mocked response, which is what I want
        client.get("/hello").apply {
            val expected = ResponseContainer("hello")
            val response = jacksonObjectMapper().readValue<ResponseContainer>(bodyAsText())
            assertEquals(HttpStatusCode.OK, status)
            assertEquals(expected.message, response.message) // This assert fails because the internal call to callOtherAPI() is not being mocked.
        }
    }

So the problem that I am facing is that while the mocked function is being mocked within the context of the test, it is not being mocked when called internally by the routing implementation.

Can someone point me to good documentation to figure this out, I've been at it for the past two hours to no avail :/

Thanks!



Solution 1:[1]

You can declare a parameter for the callOtherAPI function in the hello method. For the production and testing environment you will pass different functions in this case. Here is your code rewritten:

@Test
fun testHello() = testApplication {
    application {
        // hello(::callOtherAPI) this call will be for the production environment
        hello { ResponseContainer("hello") }
    }


    client.get("/hello").apply {
        assertEquals(HttpStatusCode.OK, status)
        assertEquals("{\"message\":\"hello\"}", bodyAsText())
    }
}

data class ResponseContainer(val message: String)

fun Application.hello(callOtherAPI: () -> ResponseContainer) {
    install(ContentNegotiation) {
        jackson()
    }

    routing {
        get("/hello") {
            call.respond(callOtherAPI())
        }
    }
}

fun callOtherAPI(): ResponseContainer {
    // networking stuff
    return ResponseContainer("some stuff")
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Aleksei Tirman