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

在Instrumented End-To-End测试中使用Test doubles:含WebView启动逻辑的Fragment测试咨询

在Instrumented End-To-End测试中使用Test doubles:含WebView启动逻辑的Fragment测试咨询

嘿,我完全懂你现在的困扰——给这个带OAuth WebView跳转的Fragment写Instrumented E2E测试,总不能每次都真的加载WebView走一遍登录流程吧?不仅慢得离谱,还容易被网络波动坑。下面给你分享两个实用的方案,用Test Double(测试替身)来搞定这个问题:

方案1:利用ActivityResultRegistry的依赖注入Mock结果

你的Fragment构造函数已经预留了registry参数,这简直是为测试量身定做的扩展点!我们可以通过传入一个模拟的ActivityResultRegistry,直接触发回调结果,完全跳过真实的WebView启动流程。

举个具体的测试代码例子(用JUnit4 + FragmentScenario):

@RunWith(AndroidJUnit4::class)
class OAuthLoginScreenTest {

    @Test
    fun `webview login success should process auth state correctly`() {
        // 创建一个模拟的ActivityResultRegistry,手动控制回调结果
        val mockRegistry = object : ActivityResultRegistry() {
            override fun <I, O> register(
                key: String,
                lifecycleOwner: LifecycleOwner,
                contract: ActivityResultContract<I, O>,
                callback: ActivityResultCallback<O>
            ): ActivityResultLauncher<I> {
                return object : ActivityResultLauncher<I>() {
                    override fun launch(input: I, options: ActivityOptionsCompat?) {
                        // 直接模拟登录成功的返回数据
                        val mockAuthIntent = Intent().apply {
                            putExtra("auth_token", "test_auth_token_123")
                            // 这里添加你业务中需要的真实返回参数
                        }
                        // 触发回调,模拟Activity返回RESULT_OK
                        callback.onActivityResult(ActivityResult(Activity.RESULT_OK, mockAuthIntent))
                    }

                    override fun unregister() {}
                    override fun getContract(): ActivityResultContract<I, O> = contract
                }
            }
        }

        // 启动Fragment时传入这个模拟的Registry
        val scenario = launchFragmentInContainer {
            OAuthLoginScreen(registry = mockRegistry)
        }

        // 触发登录操作(假设你的登录按钮id是btn_oauth_login)
        onView(withId(R.id.btn_oauth_login)).perform(click())

        // 验证后续业务逻辑是否执行,比如验证useCase是否被正确调用
        // 这里需要把useCase也改成可注入的,测试时传入Mock实例(看后面的优化建议)
        val mockUseCase = mock(AccountsApiLoginToOldPlatformUseCase::class.java)
        // 替换Fragment中的useCase后,验证调用
        verify(mockUseCase).invoke(eq("test_auth_token_123"))
    }
}

方案2:用Espresso Intent拦截模拟返回结果

如果不想改动太多现有代码,也可以用Espresso的Intent拦截功能,捕获启动WebView的Intent,直接返回模拟的结果。这种方式更贴近真实的E2E测试流程,但跳过了WebView的实际加载。

代码示例:

@RunWith(AndroidJUnit4::class)
class OAuthLoginScreenTest {

    @Before
    fun setup() {
        // 初始化Intent拦截
        Intents.init()
    }

    @After
    fun teardown() {
        // 释放资源
        Intents.release()
    }

    @Test
    fun `oauth login success triggers home screen navigation`() {
        // 拦截启动WebView登录页面的Intent(替换成你实际的WebView Activity类名)
        intending(hasComponent(WebViewLoginActivity::class.java.name))
            .respondWith(ActivityResult(Activity.RESULT_OK, Intent().apply {
                putExtra("auth_token", "test_auth_token_123")
            }))

        // 启动目标Fragment
        launchFragmentInContainer<OAuthLoginScreen>()

        // 点击登录按钮
        onView(withId(R.id.btn_oauth_login)).perform(click())

        // 验证结果:比如检查是否跳转到了主页
        onView(withId(R.id.home_screen_container)).check(matches(isDisplayed()))
    }
}

额外优化建议:让Fragment的依赖更具可测试性

看你的代码,loginToOldPlatformUseCase是在构造函数里默认初始化的,建议保持这种构造函数注入的方式,测试时可以轻松传入Mock实例,方便验证业务逻辑的调用情况。比如测试时可以这样创建Fragment:

val mockUseCase = mock(AccountsApiLoginToOldPlatformUseCase::class.java)
val fragment = OAuthLoginScreen(loginToOldPlatformUseCase = mockUseCase)

这样就能在测试中精准验证useCase是否被调用、参数是否正确,让测试覆盖更全面。


备注:内容来源于stack exchange,提问作者chriskaras

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.17 07:42:57