背景
项目初期,前后端只定义了RESTful api的json结构,在后端部署前,如何编写客户端代码?
一种手段是 客户端在data层硬编码response,这种方式的缺点很明显:
- 后端部署后,需要手动移除代码
- 无法对代码进行测试
方案
为了解决上述问题,针对不同的环境配置不同的HttpClient,对于ktor来说我们可以参考它的测试框架
ktor.io/docs/http-c… ,自定义一个MyMockEngine,然后针对mock环境配置mockHttpClient即可。
mock json存储位置: src/debug/assets/{path}/default.json
比如请求的url path部分为:/orders 对应 src/debug/assets/orders/default.json
fun mockHttpClient(context: Context): HttpClient = HttpClient(MyMockEngine(context)) {
install(ContentNegotiation) {
json(
Json {
ignoreUnknownKeys = true
explicitNulls = false
},
)
}
}
private class MyMockEngine(
val context: Context,
override val config: HttpClientEngineConfig = HttpClientEngineConfig(),
) : HttpClientEngineBase("ktor-mock") {
@OptIn(InternalAPI::class)
override suspend fun execute(data: HttpRequestData): HttpResponseData {
// 读取json
val path = data.url.encodedPath
val jsonPath = path.removePrefix("/") + "/default.json"
val mockData = runCatching {
context.assets.open(jsonPath).readBytes()
}.getOrNull()
// 构建没有配置时的response
val fallbackJson = """
{
"code": -1,
"message": "assets/$path/default.json not provided!"
}
""".trimIndent()
// 返回
return HttpResponseData(
statusCode = HttpStatusCode.OK,
requestTime = GMTDate(),
headers = headersOf(HttpHeaders.ContentType to listOf(ContentType.Application.Json.toString())),
version = HttpProtocolVersion.HTTP_1_1,
body = ByteReadChannel(mockData ?: fallbackJson.toByteArray()),
callContext = callContext(),
)
}
}
至此最简单的mock json框架已经完成,但它只支持一种返回,切换不同状态需要手动改json path。
我们可以在execute中做拦截弹出dialog让用户选择src/debug/assets/{path}/下的文件。