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

Android startActivityForResult交互逻辑单元测试方案

针对Activity导航与startActivityForResult流程的稳定单元测试方案

你遇到的仪器化测试flaky问题本质是这类测试依赖真机/模拟器的系统调度、生命周期异步分发、UI渲染时序,CI环境设备资源抢占、动画开关、系统版本差异都会导致随机失败。完全可以用Robolectric框架在本地JVM层完成所有逻辑校验,不需要依赖物理设备/模拟器,执行速度快、稳定性极强,完全覆盖你需要的所有校验点。

核心测试思路

不需要真的完成两个Activity的页面跳转,通过Robolectric提供的Shadow类捕获启动Intent、模拟Activity生命周期、模拟返回结果分发,逐段校验逻辑正确性:

  • 捕获ActivityA发出的启动Intent,校验forResult启动模式、requestCode、携带参数
  • 单独构建ActivityB实例,传入捕获的Intent,校验初始参数赋值、开关状态
  • 模拟用户操作Switch,校验变量更新
  • 捕获ActivityB返回时设置的resultCode和结果Intent
  • 手动将结果分发给ActivityA的onActivityResult,校验UI状态更新

具体实现代码

首先在模块级build.gradle添加测试依赖(仅testImplementation,不需要androidTest设备侧依赖):

testImplementation 'junit:junit:4.13.2'
testImplementation 'org.robolectric:robolectric:4.10.3'
testImplementation 'com.google.truth:truth:1.1.3'
testImplementation 'androidx.test.ext:junit:1.1.5'

测试类放在src/test/java目录下,代码如下:

@RunWith(RobolectricTestRunner::class)
class ActivityInteractTest {

    @Test
    fun testActivityAAndBInteractionFlow() {
        // 1. 初始启动ActivityA,校验初始状态
        val activityA = Robolectric.buildActivity(ActivityA::class.java).setup().get()
        val initialStarVisibility = activityA.starImageView.isVisible

        // 2. 点击跳转按钮,捕获启动的Intent
        val shadowA = shadowOf(activityA)
        val expectedRequestCode = 1
        activityA.findViewById<Button>(R.id.goToB_btn).performClick()

        // 校验确实以forResult模式启动ActivityB
        val startedIntent = shadowA.nextStartedActivityForResult
        assertThat(startedIntent.requestCode).isEqualTo(expectedRequestCode)
        assertThat(startedIntent.intent.component?.className).isEqualTo(ActivityB::class.java.name)
        // 校验Intent携带的参数正确
        assertThat(startedIntent.intent.getBooleanExtra("starVisibility", !initialStarVisibility))
            .isEqualTo(initialStarVisibility)

        // 3. 模拟启动ActivityB,校验初始状态
        val activityB = Robolectric.buildActivity(ActivityB::class.java, startedIntent.intent)
            .setup().get()
        // 校验starVisibility初始取值正确
        assertThat(activityB.starVisibility).isEqualTo(initialStarVisibility)
        val switch = activityB.findViewById<Switch>(R.id.visibilitySwitch)
        assertThat(switch.isChecked).isEqualTo(initialStarVisibility)

        // 4. 切换Switch开关,校验状态更新
        val targetVisibility = !initialStarVisibility
        switch.isChecked = targetVisibility
        assertThat(activityB.starVisibility).isEqualTo(targetVisibility)

        // 5. 模拟按返回键,捕获返回结果
        val shadowB = shadowOf(activityB)
        activityB.onBackPressed()
        // 校验B已经触发finish
        assertThat(activityB.isFinishing).isTrue()
        // 校验返回的resultCode和Intent数据
        val resultIntent = shadowB.resultIntent
        assertThat(shadowB.resultCode).isEqualTo(Activity.RESULT_OK)
        assertThat(resultIntent.getBooleanExtra("starVisibility", !targetVisibility))
            .isEqualTo(targetVisibility)

        // 6. 将结果分发给A,校验星星可见性更新
        activityA.onActivityResult(expectedRequestCode, shadowB.resultCode, resultIntent)
        assertThat(activityA.starImageView.isVisible).isEqualTo(targetVisibility)
    }
}

方案优势

  • 无flaky问题:所有逻辑在JVM层同步执行,没有设备异步调度、UI渲染延迟、系统状态干扰,CI环境执行和本地结果完全一致
  • 执行速度快:单测试用例执行时间在100ms以内,远快于仪器化测试
  • 覆盖全校验点:所有你需要验证的Intent参数、变量取值、返回结果、UI更新逻辑都能覆盖

注意:如果需要验证UI交互的真实渲染效果,可以保留极少量核心路径的仪器化测试,把逻辑校验全部放在Robolectric本地测试中,从根源上解决CI不稳定问题。

内容的提问来源于stack exchange,提问作者Dorrin-sot

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 13:51:46