在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
相关产品推荐
相关产品推荐

