Android Ktor搭建mock json环境

300 阅读1分钟

背景

项目初期,前后端只定义了RESTful api的json结构,在后端部署前,如何编写客户端代码? 一种手段是 客户端在data层硬编码response,这种方式的缺点很明显:

  1. 后端部署后,需要手动移除代码
  2. 无法对代码进行测试

方案

为了解决上述问题,针对不同的环境配置不同的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}/下的文件。