kotlinkotlin-multiplatformktorktor-client

Ktor Client Mock failed in Kotlin Multiplatform project


I created my first multiplatform example project, https://github.com/hantsy/cargotracker-regapp-kotlin-multiplatform

And use a shared lib module to shake hands with the REST APIs.

In the shared module, I tried to use ktor-client-mock in the commonTest sources to test the APIs, but it seems that it always uses the platform underlying HttpClient to call the remote rest API instead of the mocking engine in the tests. The exception indicated that it was trying to connect to the URL of the remote APIs.

The REST API-related client codes(in commonMain) are similar to the following.

class HandlingReportClient(client: HttpClient) {
    suspend fun submitReport(report: HandlingReport): HandlingResponse {
        val baseUrl = getEnvVariable("HANDLING_REPORT_SERVICE_URL") ?: DEFAULT_BASE_URL

        val response = client.request("$baseUrl/rest/handling/reports") {
            method = HttpMethod.Get
            contentType(ContentType.Application.Json)
            accept(ContentType.Application.Json)
            setBody(report)
        }

        return if (response.status == HttpStatusCode.OK) {
            response.body(typeInfo<HandlingResponse.Success>())
        } else {
            response.body(typeInfo<HandlingResponse.Error>())
        }
    }
}

And the testing codes(in commonTest):

class HandlingReportClientTest {

    val report = HandlingReport(
        completionTime = "1/10/2025 27:59",
        trackingId = "AAA",
        eventType = "LOAD",
        unLocode = "CHSHA",
        voyageNumber = "A0123"
    )

    @Test
    fun `submit report`() = runTest {
        val mockEngine = MockEngine { request ->
            respond(
                content = """""",
                status = HttpStatusCode.Companion.OK,
                headers = headersOf(HttpHeaders.ContentType, "application/json")
            )
        }
        val client = HandlingReportClient(HttpClient(mockEngine))
        val result = client.submitReport(report)
        result is HandlingResponse.Success
        assertEquals("OK", result.toString())
    }

    @Test
    fun `submit report(with respond error)`() = runTest {
        val mockEngine = MockEngine { request ->
            respond(
                content = """
                    {
                        "message":"err"
                    }
                """.trimIndent(),
                status = HttpStatusCode.Companion.BadRequest,
                headers = headersOf(HttpHeaders.ContentType, "application/json")
            )
        }
        val client = HandlingReportClient(HttpClient(mockEngine))
        val result = client.submitReport(report)
        result is HandlingResponse.Error
        assertEquals("err", (result as HandlingResponse.Error).message)
    }

}

The complete codes: https://github.com/hantsy/cargotracker-regapp-kotlin-multiplatform/tree/master/shared

I used an expect/actual function to get the HttpClient for different platforms. It seems when running the tests, the MockEngine in the tests did not replace the one on the platform.


Solution

  • The problem is that in the shared/src/commonMain/kotlin/cargotracker/regapp/shared/HandlingReportClient.kt file, the HTTP client instance used for making the requests is declared as a top-level property. The one that is passed for the HandlingReportClient class constructor is unused.

    To solve the problem, use the HTTP client defined in the class.