Firebase Test Lab中GrantPermissionRule授权失败引发JUnit+Espresso仪器化测试崩溃的问题问询
我之前也碰到过一模一样的情况——本地模拟器/真机上测试跑的顺风顺水,一提交到Firebase Test Lab就崩。结合你的日志和描述,问题确实大概率出在权限授权环节,下面给你拆解原因和可行的解决办法:
为什么Firebase Test Lab会出现这个问题?
Firebase Test Lab的设备和本地环境有几个关键差异:
- 对于Android 6.0+的危险权限(比如你用到的位置、相机、录音),Test Lab的设备不会自动弹出系统授权对话框,也不会默认授予这些权限。你的
GrantPermissionRule在本地能生效,是因为本地环境允许测试框架自动授予权限,但Test Lab的安全约束限制了这个操作。 - 如果测试流程卡在权限请求环节(没人手动点击授权),一段时间后系统会回收未交互的Activity,最终就会抛出
NoActivityResumedException,这和你看到的视频里应用崩溃的现象完全吻合。 - 另外,部分Test Lab设备可能缺少硬件支持(比如部分模拟器没有麦克风),请求这类权限时也会触发异常。
可行的解决办法
1. 用adb命令手动授予权限(最直接的方案)
放弃GrantPermissionRule,改用UiAutomation执行adb命令在测试启动前手动授予权限,这个方法在Test Lab的大多数设备上都能生效。你可以在测试类的@Before方法里添加这段代码:
@Before fun setupPermissions() { val instrumentation = InstrumentationRegistry.getInstrumentation() val targetPackage = instrumentation.targetContext.packageName val permissions = arrayOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO ) permissions.forEach { permission -> instrumentation.uiAutomation.executeShellCommand("pm grant $targetPackage $permission") } }
注:Test Lab的测试环境默认已经给测试进程配置了android.permission.GRANT_RUNTIME_PERMISSIONS权限,所以这个命令能正常执行。
2. 调整测试流程,避免依赖敏感权限
如果你的测试不需要实际使用这些权限对应的功能(比如只是测试UI跳转,不需要调用相机),可以用Mock替代真实的权限依赖:
- 用Mockito或其他框架Mock位置服务、相机的返回结果
- 在测试前禁用依赖这些权限的功能模块,让测试流程不触发权限请求
3. 确保Activity先于权限请求启动
检查你的测试代码,确保在处理权限之前,Activity已经被正确启动。比如用ActivityScenario明确启动Activity:
@Test fun testFragmentInteraction() { // 先启动Activity val scenario = ActivityScenario.launch(MainActivity::class.java) // 再执行测试操作或权限相关逻辑 onView(withId(R.id.fragment_container)).check(matches(isDisplayed())) }
避免GrantPermissionRule的执行时机早于Activity启动,导致Activity还没就绪就因为权限问题卡住。
4. 针对性排查单个权限
你可以尝试逐个移除权限规则里的权限,在Test Lab中重新运行测试,找到具体触发崩溃的权限:
- 比如先移除
RECORD_AUDIO,如果测试正常运行,说明这个权限是问题所在,可能是Test Lab设备不支持,这时可以考虑跳过该权限的测试,或者在测试前检查设备是否支持录音功能。
关于Firebase Test Lab的权限限制文档
Firebase Test Lab的官方文档里明确提到,对于危险权限,测试需要主动处理授权流程,不能依赖系统弹窗。另外,部分特殊权限(比如SYSTEM_ALERT_WINDOW)在Test Lab里是无法授予的,但你用到的这几个权限大多支持通过adb命令授予。
内容的提问来源于stack exchange,提问作者Daniel Schröder

