如何用Android Espresso模拟Credential Manager登录流程
问题:如何模拟Credential Manager的登录流程进行测试?
迁移到Credential Manager后,我找不到测试不同登录场景的方法。此前使用GoogleSignIn时,我可以借助Espresso的intending机制返回模拟响应完成测试,无需真实邮箱密码,仅根据输入的tokenId执行后续逻辑,代码如下:
protected fun setupAndClickSignInButton(signInAccountJson: String) { val intent = Intent() intent.putExtra("googleSignInAccount", GoogleSignInAccount.zab(signInAccountJson)) val signInIntent = GoogleSignIn.getClient( getTargetContext(), GoogleApiClientHelper.generateGoogleSignInOptions() ).signInIntent intending(hasComponent(signInIntent.component)) .respondWith(Instrumentation.ActivityResult(Activity.RESULT_OK, intent)) onView(withId(R.id.buttonSignInWithGmail)) .check(matches(isDisplayed())) .perform(click()) }
protected fun genMockGoogleSignInAccountJson( email: String, tokenId: String = genMockGoogleSignInTokenId(email) ): String { return "{\n" + " \"id\":\"111111111111111111111\",\n" + " \"tokenId\":\"$tokenId\",\n" + " \"email\":\"" + email + "\"\n" + " \"displayName\":\"Test\",\n" + " \"givenName\":\"Test\",\n" + " \"familyName\":\"Test\",\n" + " \"photoUrl\":\"https:\/\/example.com\",\n" + " \"expirationTime\":1572537339,\n" + " \"obfuscatedIdentifier\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n" + " \"grantedScopes\":[\n" + " \"email\",\n" + " \"https:\/\/mail.google.com\/\",\n" + " \"https:\/\/www.googleapis.com\/auth\/userinfo.email\",\n" + " \"https:\/\/www.googleapis.com\/auth\/userinfo.profile\",\n" + " \"openid\",\n" + " \"profile\"\n" + " ]\n" + "}" }
现在认证与授权流程已分离,授权基于Google的Android用户数据授权方案,可通过onActivityResult方式模拟,但Credential Manager采用AIDL机制,不清楚如何模拟其登录流程,请问是否可行?
解决方案
可行,以下是几种实用的模拟方案:
1. 利用Google官方测试库生成模拟凭证
Google提供了专门的测试库com.google.android.libraries.identity.googleid:googleid-test,可直接创建符合格式的模拟Google ID凭证,结合Mockito拦截CredentialManager的调用返回模拟结果:
// 先在build.gradle中添加测试依赖 // androidTestImplementation "com.google.android.libraries.identity.googleid:googleid-test:1.0.0" import com.google.android.libraries.identity.googleid.testing.TestGoogleIdTokenCredential import org.mockito.Mockito.`when` import org.mockito.Mockito.mock // 创建模拟凭证 val mockCredential = TestGoogleIdTokenCredential.builder() .setIdToken("your-custom-mock-token-id") .setEmail("test@example.com") .setDisplayName("Test User") .build() // Mock CredentialManager实例 val mockCredentialManager = mock(CredentialManager::class.java) `when`(mockCredentialManager.getCredential(any(), any(), any())).thenAnswer { invocation -> val callback = invocation.getArgument<CredentialManager.CredentialCallback>(2) callback.onCredentialRetrieved(mockCredential) } // 将Mock实例注入到你的认证管理类中(需依赖注入支持) YourAuthProvider.setCredentialManager(mockCredentialManager) // 执行登录操作 onView(withId(R.id.buttonSignInWithGmail)) .check(matches(isDisplayed())) .perform(click())
2. 用Espresso拦截CredentialManager的启动Intent
如果你的登录流程是通过StartActivityForResult触发的,可继续用Espresso的intending机制拦截目标Intent,返回模拟的凭证结果:
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential // 构造符合要求的模拟凭证 val mockCredential = GoogleIdTokenCredential.createFrom( "mock-token-id", "test@example.com", "Test User" ) // 封装结果Intent val resultIntent = Intent() resultIntent.putExtra(CredentialManager.EXTRA_CREDENTIAL, mockCredential) // 拦截CredentialManager的启动Intent(匹配Google服务包名) intending(hasPackage("com.google.android.gms")) .respondWith(Instrumentation.ActivityResult(Activity.RESULT_OK, resultIntent)) // 执行登录按钮点击 onView(withId(R.id.buttonSignInWithGmail)) .check(matches(isDisplayed())) .perform(click())
3. 实现Fake CredentialManager替换真实实例
如果应用采用了依赖注入框架(如Hilt、Dagger),可以在测试环境中提供一个Fake实现,直接返回预设的模拟结果:
// Fake CredentialManager实现 class FakeCredentialManager : CredentialManager { override fun getCredential( context: Context, request: CredentialRequest, callback: CredentialManager.CredentialCallback ) { // 根据测试场景返回不同的模拟凭证 val mockCredential = TestGoogleIdTokenCredential.builder() .setIdToken("mock-token-123") .setEmail("test@example.com") .build() callback.onCredentialRetrieved(mockCredential) } // 实现其他必要的接口方法(如getCredentialAsync等) } // 在测试模块中绑定Fake实现(以Hilt为例) @Module @TestInstallIn(components = [SingletonComponent::class], replaces = [RealCredentialManagerModule::class]) class FakeCredentialManagerModule { @Provides fun provideCredentialManager(): CredentialManager = FakeCredentialManager() }
内容的提问来源于stack exchange,提问作者Den
相关产品推荐
相关产品推荐

