渐进式编程课
KMP 目录
第 15 课2026.07.31

共享状态持有者决定 Android 和 iOS 最终看到的页面状态。把测试写在 commonTest,可以用一套用例验证两端共同依赖的状态转换,不需要启动模拟器或访问真实服务器。

第 15 课:测试共享状态持有者

  • 日期:2026-07-31
  • 课程序号:15
  • 知识点:使用 Fake Repository 和 runTest 验证共享状态

用途

共享状态持有者决定 Android 和 iOS 最终看到的页面状态。把测试写在 commonTest,可以用一套用例验证两端共同依赖的状态转换,不需要启动模拟器或访问真实服务器。

核心概念

  • 测试向 UserStore 注入可控的 Fake Repository。
  • runTest 用来执行包含挂起函数的协程测试。
  • 通过 state.value 检查当前 StateFlow 状态。
  • 测试关注输入和可观察结果,不读取私有 _state。
  • 成功与失败应分别验证,避免只覆盖理想链路。

示例代码

commonTest 需要 Kotlin 测试和协程测试依赖:

kotlin
kotlin {
    sourceSets {
        commonTest.dependencies {
            implementation(kotlin("test"))
            implementation(libs.kotlinx.coroutines.test)
        }
    }
}

准备一个返回固定结果的 Fake:

kotlin
class FakeUserRepository(
    private val result: DataResult<User>,
) : UserRepository {
    override suspend fun loadUser(id: Long): DataResult<User> {
        return result
    }
}

commonTest/UserStoreTest.kt:

kotlin
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals

class UserStoreTest {
    @Test
    fun startsWithIdleState() {
        val repository = FakeUserRepository(
            DataResult.Failure(DataError.Network),
        )
        val store = UserStore(repository)

        assertEquals(UserUiState.Idle, store.state.value)
    }

    @Test
    fun exposesContentAfterSuccessfulLoad() = runTest {
        val user = User(
            id = 1L,
            displayName = "Ada",
        )
        val repository = FakeUserRepository(
            DataResult.Success(user),
        )
        val store = UserStore(repository)

        store.load(id = 1L)

        assertEquals(
            UserUiState.Content(user),
            store.state.value,
        )
    }
}

Fake 返回确定数据,因此测试结果稳定,也能证明 UserStore 的状态来自 Repository 结果。

5 分钟练习

增加一个测试:Fake Repository 返回 DataError.Network,执行 load() 后验证状态为 UserUiState.Error(DataError.Network)。

参考答案

kotlin
@Test
fun exposesErrorAfterFailedLoad() = runTest {
    val repository = FakeUserRepository(
        DataResult.Failure(DataError.Network),
    )
    val store = UserStore(repository)

    store.load(id = 1L)

    assertEquals(
        UserUiState.Error(DataError.Network),
        store.state.value,
    )
}

测试直接验证数据层错误是否被正确映射,不需要让 UI 根据空内容判断失败。

与上一课的联系

上一课建立了 UserStore 和统一 UserUiState;本课利用此前学习的构造器注入与 Fake,验证 Repository 的成功、失败结果会变成正确的共享状态。