Testing Http Client (MockEngine)

Estimated reading time: 1 minute

Ktor exposes a MockEngine for the HttpClient. This engine allows to simulate HTTP calls without actually connecting to the endpoint. It allows to set a code block, that can handle the request, and generates a response.

This engine is defined in the class io.ktor.client.engine.mock.MockEngine in the artifact io.ktor:ktor-client-mock:$ktor_version,io.ktor:ktor-client-mock-jvm:$ktor_version,io.ktor:ktor-client-mock-js:$ktor_version,io.ktor:ktor-client-mock-native:$ktor_version.
dependencies { api "io.ktor:ktor-client-mock:$ktor_version" api "io.ktor:ktor-client-mock-jvm:$ktor_version" api "io.ktor:ktor-client-mock-js:$ktor_version" api "io.ktor:ktor-client-mock-native:$ktor_version" }
dependencies { testCompile("io.ktor:ktor-client-mock:$ktor_version,io.ktor:ktor-client-mock-jvm:$ktor_version,io.ktor:ktor-client-mock-js:$ktor_version,io.ktor:ktor-client-mock-native:$ktor_version") }
<project> ... <dependencies> <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-mock</artifactId> <version>${ktor.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-mock</artifactId> <version>${ktor.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-mock</artifactId> <version>${ktor.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-mock</artifactId> <version>${ktor.version}</version> <scope>test</scope> </dependency> </dependencies> </project>

Usage

The usage is very simple: the MockEngine class has a operator invoke method, that receives a block/callback that will handle the request. This callback receives an HttpRequest as this, a HttpClientCall as a parameter, and must return a MockHttpResponse. The MockHttpResponse, includes the HTTP Status Code, a ByteReadChannel with the body, and a set of headers.

A sample illustrating this:

val httpMockEngine = MockEngine { call -> // this: HttpRequest, call: HttpClientCall
    when (url.fullUrl) {
        "https://example.org/" -> {
            MockHttpResponse(
                call,
                HttpStatusCode.OK,
                ByteReadChannel("Hello World!".toByteArray(Charsets.UTF_8)),
                headersOf("Content-Type" to listOf(ContentType.Text.Plain.toString()))
            )
        }
        else -> {
            error("Unhandled ${url.fullUrl}")
        }
    }
}

val client = HttpClient(httpMockEngine)

private val Url.hostWithPortIfRequired: String get() = if (port == protocol.defaultPort) host else hostWithPort
private val Url.fullUrl: String get() = "${protocol.name}://$hostWithPortIfRequired$fullPath"