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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 12:02:18