You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

KMM(Kotlin多平台移动)网络调用单元测试实现求助

KMM跨平台网络单元测试问题解决方案

问题根源分析

  1. commonTest不等待异步请求完成:你的testApi方法内部创建了独立的CoroutineScope,runBlocking无法追踪该作用域内的协程执行,导致测试直接走完流程,不等网络请求返回。
  2. Android仪器测试网络错误:你请求的jsonplaceholder.typicode.com用的是HTTP明文协议,Android 9+默认禁止明文流量,而仪器测试的Manifest可能未配置相关允许项,导致域名解析失败。

一、重构testApi方法,实现协程作用域可注入

把内部硬编码的CoroutineScope改成参数传入,让测试代码可以控制协程的生命周期:

// 修改后的testApi方法
fun testApi(
    coroutineScope: CoroutineScope, // 新增作用域参数
    success: () -> Unit,
    failure: (Error) -> Unit
) {
    val manager = getAuthorizationHTTPManager("jsonplaceholder.typicode.com", false)
    if (manager == null) {
        val error = BLError.error(BLErrorCodes.consumerCloudPostOAuthFailed,  "Unable to create a session manager")
        BLLog.logError(error)
        failure(error)
        return
    }
    // 使用传入的作用域启动协程,不再自行创建
    coroutineScope.launch(Dispatchers.IO)  {
        try {
            val response = manager.get("/posts")

            Napier.d(response.body<String>().toString())
            if(response.status.value in 200 .. 299){
                success()
            }
            else {
                val json = JsonObject(response.body())
                val error = BLError.error(BLErrorCodes.consumerCloudPostOAuthFailed, json["error"].toString())
                failure(error)
            }
        } catch (e: Exception) {
            val error = BLError.error(BLErrorCodes.consumerCloudPostOAuthFailed, e.toString())
            failure(error)
        }
    }
}

业务代码中调用时,传入对应生命周期的作用域即可(比如Android端用viewModelScope,iOS端用自定义的作用域):

// Android业务代码示例
testApi(viewModelScope, success = { /* 成功逻辑 */ }, failure = { /* 失败逻辑 */ })

二、修复commonTest异步等待问题

使用kotlinx-coroutines-test库的runTest方法,它会自动等待测试作用域内的所有协程执行完毕。

1. 添加测试依赖

在shared模块的build.gradle.kts中添加协程测试库:

sourceSets["commonTest"].dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3") // 可替换为最新版本
}

2. 编写测试代码

import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertNull
import kotlin.test.assertTrue

class CommonApiTest {
    private val consumerCloud = YourConsumerCloudInstance() // 替换为你的实例

    @Test
    fun testApiSuccess() = runTest {
        var isSuccess = false
        var error: Error? = null

        consumerCloud.testApi(
            coroutineScope = this, // 传入runTest提供的TestScope
            success = { isSuccess = true },
            failure = { error = it }
        )

        // 断言结果
        assertNull(error)
        assertTrue(isSuccess)
    }
}

runTest会自动阻塞直到所有子协程执行完成,解决测试提前结束的问题。


三、修复Android仪器测试网络错误

1. 允许明文流量

在androidInstrumentedTest目录下的AndroidManifest.xml中添加配置,允许HTTP明文请求:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="your.package.name"> <!-- 替换为你的应用包名 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:usesCleartextTraffic="true">
    </application>
</manifest>

2. 优化仪器测试代码(可选)

现在testApi支持传入作用域,仪器测试也可以用runTest替代CountDownLatch,代码更简洁:

import kotlinx.coroutines.test.runTest
import org.junit.Test
import kotlin.test.assertNull
import kotlin.test.assertTrue

class InstrumentedApiTest {
    private val consumerCloud = YourConsumerCloudInstance()

    @Test
    fun testApiSuccess() = runTest {
        var isSuccess = false
        var error: Error? = null

        consumerCloud.testApi(
            coroutineScope = this,
            success = { isSuccess = true },
            failure = { error = it }
        )

        assertNull(error)
        assertTrue(isSuccess)
    }
}

额外建议

  • 单元测试阶段推荐用MockK等工具Mock Ktor客户端,避免依赖真实服务器,提升测试稳定性和速度。
  • 确保getAuthorizationHTTPManager在测试环境中正确初始化Ktor客户端,没有错误配置baseUrl或禁用网络。

内容的提问来源于stack exchange,提问作者Ritika

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 19:01:29