Testing Http Client (MockEngine)

Ktor exposes a MockEngine for the HttpClient. This engine allows simulating 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.

  1. dependencies {
  2. api "io.ktor:ktor-client-mock:$ktor_version"
  3. api "io.ktor:ktor-client-mock-jvm:$ktor_version"
  4. api "io.ktor:ktor-client-mock-js:$ktor_version"
  5. api "io.ktor:ktor-client-mock-native:$ktor_version"
  6. }
  1. dependencies {
  2. testImplementation("io.ktor:ktor-client-mock:$ktor_version")
  3. testImplementation("io.ktor:ktor-client-mock-jvm:$ktor_version")
  4. testImplementation("io.ktor:ktor-client-mock-js:$ktor_version")
  5. testImplementation("io.ktor:ktor-client-mock-native:$ktor_version")
  6. }
  1. <project>
  2. ...
  3. <dependencies>
  4. <dependency>
  5. <groupId>io.ktor</groupId>
  6. <artifactId>ktor-client-mock</artifactId>
  7. <version>${ktor.version}</version>
  8. <scope>test</scope>
  9. </dependency>
  10. <dependency>
  11. <groupId>io.ktor</groupId>
  12. <artifactId>ktor-client-mock</artifactId>
  13. <version>${ktor.version}</version>
  14. <scope>test</scope>
  15. </dependency>
  16. <dependency>
  17. <groupId>io.ktor</groupId>
  18. <artifactId>ktor-client-mock</artifactId>
  19. <version>${ktor.version}</version>
  20. <scope>test</scope>
  21. </dependency>
  22. <dependency>
  23. <groupId>io.ktor</groupId>
  24. <artifactId>ktor-client-mock</artifactId>
  25. <version>${ktor.version}</version>
  26. <scope>test</scope>
  27. </dependency>
  28. </dependencies>
  29. </project>

Usage

The usage is straightforward: the MockEngine class has a method addHandler in MockEngineConfig, that receives a block/callback that will handle the request. This callback receives an HttpRequest as a parameter, and must return a HttpResponseData. There are many helper methods to construct the response.

Full API description and list of helper methods could be found here.

A sample illustrating this:

  1. val client = HttpClient(MockEngine) {
  2. engine {
  3. addHandler { request ->
  4. when (request.url.fullUrl) {
  5. "https://example.org/" -> {
  6. val responseHeaders = headersOf("Content-Type" to listOf(ContentType.Text.Plain.toString()))
  7. respond("Hello, world", headers = responseHeaders)
  8. }
  9. else -> error("Unhandled ${request.url.fullUrl}")
  10. }
  11. }
  12. }
  13. }
  14. private val Url.hostWithPortIfRequired: String get() = if (port == protocol.defaultPort) host else hostWithPort
  15. private val Url.fullUrl: String get() = "${protocol.name}://$hostWithPortIfRequired$fullPath"