Kotlin+RxJava环境下无法Mock Android AccountManager类求助
我来帮你排查下这个Mock AccountManager失败的问题,大概率是因为几个常见的配置或逻辑匹配问题,咱们一步步来解决:
1. 解决Mockito无法Mock Final类的问题
Android的AccountManager是系统级的final类,而Mockito默认版本(未开启inline模式)无法Mock final类。这是最常见的原因,你需要开启Mockito的inline支持:
方法一:添加Mockito Inline依赖
在你的app模块build.gradle(或build.gradle.kts)中替换或添加Mockito的inline依赖:
// Groovy testImplementation 'org.mockito:mockito-inline:4.11.0' // 或者Kotlin DSL testImplementation("org.mockito:mockito-inline:4.11.0")
版本可以根据你项目的Mockito主版本调整,确保和主版本兼容。
方法二:手动配置Mock Maker
如果不想用inline依赖,可以在src/test/resources/mockito-extensions目录下创建一个名为org.mockito.plugins.MockMaker的文件,文件内容仅一行:
mock-maker-inline
这样就能让Mockito支持Mock final类了。
2. 验证方法调用的参数匹配
看你的测试代码和待测试方法,这里有个潜在的参数不匹配问题:
- 测试中你Mock的是
accountManager.getAccountsByType(BuildConfig.APPLICATION_ID) - 但待测试方法里调用的是
accountManager.getAccountsByType(ACCOUNT_TYPE)
你需要确认ACCOUNT_TYPE的定义是否等于BuildConfig.APPLICATION_ID。如果不等,Mock的参数不匹配,getAccountsByType会返回null,导致循环根本不会执行,自然不会触发onNext,blockingFirst就会抛出异常。
解决方法:要么把测试中的参数改成ACCOUNT_TYPE,要么确保ACCOUNT_TYPE的常量值就是BuildConfig.APPLICATION_ID。
3. 补充Mock后续的关键方法调用
即使getAccountsByType返回了正确的Account数组,待测试方法里还有两个关键条件需要满足:
account.name == country.appName:要确保你定义的ACCOUNT_1的name等于Country.GERMANY.appName,否则条件不成立,不会执行更新逻辑。user.id == userId:这里的userId来自accountManager.getUserData(account, KEY_USER_ID),如果没有Mock这个方法,它默认返回null,导致条件不成立。
你需要在测试中添加这个方法的Mock:
// 假设user.id是已知的字符串,比如"123" Mockito.`when`(accountManager.getUserData(ACCOUNT_1, KEY_USER_ID)).thenReturn(user.id)
4. 完善测试的验证逻辑
除了断言返回值,你可以添加Mockito的verify来确认AccountManager的方法是否真的被调用,这样能更准确地定位问题:
// 验证setUserData是否被调用了指定的参数 Mockito.verify(accountManager).setUserData(ACCOUNT_1, KEY_USER_NAME, user.username) Mockito.verify(accountManager).setPassword(ACCOUNT_1, user.secret)
最终调整后的测试示例
@Test fun testUpdate(){ // 确保ACCOUNT_1的name和Country.GERMANY.appName一致 val ACCOUNT_1 = Account(Country.GERMANY.appName, BuildConfig.APPLICATION_ID) // Mock getAccountsByType返回正确的Account数组 Mockito.`when`(accountManager.getAccountsByType(ACCOUNT_TYPE)).thenReturn(arrayOf(ACCOUNT_1)) // Mock getUserData返回匹配的userId Mockito.`when`(accountManager.getUserData(ACCOUNT_1, KEY_USER_ID)).thenReturn(user.id) val result = userLocalDataSource.update(user, Country.GERMANY).blockingFirst() assertEquals(user.username, result.username) // 验证更新方法是否被调用 Mockito.verify(accountManager).setUserData(ACCOUNT_1, KEY_USER_NAME, user.username) }
内容的提问来源于stack exchange,提问作者Sanjay Mallur

