💨

Ktor 2.0 の testApplication で client の共通処理を書く

2022/06/05に公開

最近 Ktor 2.0.2 にアップデートしたところ、今までテストで使用していたメソッドが非推奨になっていた。ガイドを見るとtestApplicationを使用したテストが記載されていた。

https://ktor.io/docs/testing.html

import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.testing.*
import kotlin.test.*

class ApplicationTest {
    @Test
    fun testRoot() = testApplication {
        val response = client.get("/")
        assertEquals(HttpStatusCode.OK, response.status)
        assertEquals("Hello, world!", response.bodyAsText())
    }
}

このようにtestApplicationで定義されている client を使用して http request を実行する。client をカスタマイズする場合のサンプルも書いてあったのだが、各テストケースで毎回 client をカスタマイズをするのは微妙だと思ったので、テスト用の関数を作った。以下の関数を使用すると Authorization ヘッダにBearer tokenという文字列が設定された状態で http request を実行する。作成したソースコードは github のリポジトリを参照。
https://github.com/mizumura3/ktor-test-sample

fun customTest(
    url: String,
    httpMethod: HttpMethod,
    body: Any? = null,
    assertBlock: suspend (response: HttpResponse) -> Unit
) {
    return runBlocking {
        val testApp = TestApplication {
            application {
                configureRouting()
            }
        }
        try {
            val testClient = testApp.createClient {
                install(DefaultRequest) {
                    header("Authorization", "Bearer token") // ちゃんとした jwt を生成して設定する
                    contentType(ContentType.Application.Json)
                }
                install(ContentNegotiation) {
                    jackson {
                        // jackson の設定を追記する
                    }
                }
            }
            runBlocking {
                val response = testClient.request(urlString = url) {
                    method = httpMethod
                    setBody(body)
                }
                assertBlock(response)
            }
        } finally {
            testApp.stop()
        }
    }
}

レスポンスで Authorization header を返却する routing を定義し Bearer token が返却されることを確認した。

fun Application.configureRouting() {
    routing {
        get("/") {
            call.respondText("Hello World!")
        }
        get("/header_test") {
            call.respondText(call.request.headers["Authorization"]!!)
        }
    }
}

class ApplicationTest {
    @Test
    fun testRoot() = testApplication {
        application {
            configureRouting()
        }
        client.get("/").apply {
            assertEquals(HttpStatusCode.OK, status)
            assertEquals("Hello World!", bodyAsText())
        }
    }


    @Test
    fun customTest() {
        customTest(url = "/header_test", httpMethod = HttpMethod.Get) { response ->
            response.apply {
                assertEquals(HttpStatusCode.OK, status)
                // Authorization ヘッダの中身が返却されることを確認
                assertEquals("Bearer token", bodyAsText())
            }
        }
    }
}

Discussion