【发布时间】:2025-11-25 03:10:01
【问题描述】:
我正在Ktor client 之上创建服务。我的有效负载是 XML,因此我的客户端的简化版本如下所示:
class MavenClient(private val client : HttpClient) {
private suspend fun getRemotePom(url : String) =
try{ MavenClientSuccess(client.get<POMProject>(url)) }catch (e: Exception) { MavenClientFailure(e)
}
companion object {
fun getDefaultClient(): HttpClient {
return HttpClient(Apache) {
install(JsonFeature) {
serializer = JacksonSerializer(jackson = kotlinXmlMapper)
accept(ContentType.Text.Xml)
accept(ContentType.Application.Xml)
accept(ContentType.Text.Plain)
}
}
}
}
}
注意自定义 XMLMapper 的使用,附加到自定义数据类。
我想测试这个类,关注documentation。
我的测试客户端最终得到以下代码:
private val mockClient = HttpClient(MockEngine) {
engine {
addHandler { request ->
when (request.url.fullUrl) {
"https://lengrand.me/minimal/1.2/minimal-1.2.pom" -> {
respond(minimalResourceStreamPom.readBytes()
, headers = headersOf("Content-Type" to listOf(ContentType.Application.Xml.toString())))
}
"https://lengrand.me/unknown/1.2/unknown-1.2.pom" -> {
respond("", HttpStatusCode.NotFound)
}
else -> error("Unhandled ${request.url.fullUrl}")
}
}
}
// TODO : How do I avoid repeating this again ? That's my implementation?!
install(JsonFeature) {
serializer = JacksonSerializer(jackson = PomParser.kotlinXmlMapper)
accept(ContentType.Text.Xml)
accept(ContentType.Application.Xml)
accept(ContentType.Text.Plain)
}
}
private val Url.hostWithPortIfRequired: String get() = if (port == protocol.defaultPort) host else hostWithPort
private val Url.fullUrl: String get() = "${protocol.name}://$hostWithPortIfRequired$fullPath"
private val mavenClient = MavenClient(mockClient)
现在,我不担心 Mapper 本身,因为我直接测试它。
然而困扰我的是,我基本上必须复制客户的完整逻辑来测试行为?
这似乎很脆弱,因为例如,如果我明天搬到 Json,它将导致我的测试失败并且必须更新。例如,如果我开始使用 Response Validation 也是如此。
对于我使用defaultRequest 的另一个客户来说更是如此,我也必须完全复制它:
private val mockClient = HttpClient(MockEngine) {
install(JsonFeature) {
serializer = JacksonSerializer(mapper)
accept(ContentType.Application.Json)
}
defaultRequest {
method = HttpMethod.Get
host = "api.github.com"
header("Accept", "application/vnd.github.v3+json")
if (GithubLogin().hasToken()) header("Authorization", GithubLogin().authToken)
}
我做错了吗?我测试太多了吗?我很好奇如何改进这一点。
非常感谢您的意见!
P.S : 不相关,但关于在 Ktor 上进行测试的页面提到将依赖项添加到 implementation。听起来我应该使用 testImplementation 来避免将 lib 与我的应用程序一起发送?
【问题讨论】:
-
为什么不将公共代码提取到
getDefaultClientConfig(): HttpClientConfig<*>.() -> Unit函数中呢?所以你可以用fun getDefaultClient() = HttpClient(Apache) { getDefaultClientConfig(); /* and some specific config for production */ }和private val mockClient = HttpClient(MockEngine) { getDefaultClientConfig(); /* and some specific config for tests */}调用它
标签: unit-testing kotlin testing ktor ktor-client