如何为Firebase Dynamic Link构建单元测试并实现链接唤起APP自动化测试
我之前也踩过这个坑——真机上通过邮件打开Firebase动态链接完全正常,但在自动化测试里死活触发不了App唤起,折腾了好一阵才捋清楚问题所在。下面分享我总结的单元测试写法,以及解决自动化测试中唤起问题的具体方案:
一、单元测试:聚焦动态链接解析逻辑
单元测试的核心是验证你的代码对动态链接的解析逻辑是否正确,而非测试系统级的App唤起(这属于UI自动化范畴)。毕竟单元测试跑在本地JVM,没有Android系统的Intent分发环境。
这里用Mockito来Mock Firebase的依赖,避免依赖真实SDK:
// 先添加测试依赖 testImplementation 'org.mockito:mockito-core:4.8.1' testImplementation 'com.google.firebase:firebase-dynamic-links-testing:21.0.0' @RunWith(MockitoJUnitRunner::class) class DynamicLinkParserTest { @Mock private lateinit var mockFirebaseDynamicLinks: FirebaseDynamicLinks @Before fun setup() { // 让FirebaseDynamicLinks.getInstance()返回我们的Mock实例 `when`(FirebaseDynamicLinks.getInstance()).thenReturn(mockFirebaseDynamicLinks) } @Test fun `parse valid dynamic link returns correct deep link`() { // 构造模拟的动态链接数据 val expectedDeepLink = Uri.parse("myapp://product/123") val mockPendingLink = mock(PendingDynamicLinkData::class.java) `when`(mockPendingLink.link).thenReturn(expectedDeepLink) `when`(mockFirebaseDynamicLinks.getDynamicLink(any())).thenReturn(Task.forResult(mockPendingLink)) // 调用你的解析方法 val result = DynamicLinkParser.parse(mock(Intent::class.java)) // 验证解析结果符合预期 assertEquals(expectedDeepLink, result) } @Test fun `parse invalid dynamic link returns null`() { `when`(mockFirebaseDynamicLinks.getDynamicLink(any())).thenReturn(Task.forResult(null)) val result = DynamicLinkParser.parse(mock(Intent::class.java)) assertNull(result) } }
二、自动化测试:解决App唤起失败的问题
测试中直接加载URL无法唤起App,本质是因为测试进程和系统Intent分发是隔离的,不会触发系统的App关联规则。我们需要分两种场景处理:
场景1:仅测试App内的链接处理逻辑
如果只需要验证App接收到动态链接后的页面跳转,无需模拟外部App唤起,可以直接构造对应的Intent传入:
@RunWith(AndroidJUnit4::class) class DynamicLinkNavigationTest { @Rule @JvmField val activityRule = ActivityScenarioRule(MainActivity::class.java) @Test fun `dynamic link triggers product detail page`() { // 构造包含深层链接的Intent val deepLinkIntent = Intent(Intent.ACTION_VIEW, Uri.parse("myapp://product/123")) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) // 给当前Activity传入这个Intent activityRule.scenario.onActivity { activity -> activity.startActivity(deepLinkIntent) } // 用Espresso验证页面跳转结果 onView(withId(R.id.product_detail_title)) .check(matches(isDisplayed())) .check(matches(withText("Product #123"))) } }
场景2:端到端测试完整唤起流程(从外部App到你的App)
如果需要完整模拟“从浏览器/邮件打开链接唤起App”的流程,必须用UI Automator操作系统级UI:
@RunWith(AndroidJUnit4::class) class DynamicLinkEndToEndTest { private lateinit var uiDevice: UiDevice @Before fun setup() { uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) // 回到主屏幕,准备测试 uiDevice.pressHome() val launcherPackage = uiDevice.launcherPackageName assertNotNull(launcherPackage) uiDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), 5000) } @Test fun `open dynamic link in chrome launches app`() { // 启动Chrome并加载你的动态链接 val chromeIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://your-dynamic-link.page.link/abc123")) .setPackage("com.android.chrome") .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) InstrumentationRegistry.getInstrumentation().context.startActivity(chromeIntent) // 等待Chrome加载完成 uiDevice.wait(Until.hasObject(By.pkg("com.android.chrome")), 5000) // 点击系统弹出的"打开应用"对话框按钮(部分设备是英文"Open",需根据测试环境调整) val openAppBtn = uiDevice.findObject(By.text("打开")) if (openAppBtn.exists()) openAppBtn.click() // 验证你的App被唤起并跳转到目标页面 uiDevice.wait(Until.hasObject(By.pkg("com.your.app.package")), 5000) onView(withId(R.id.product_detail_title)) .check(matches(isDisplayed())) } }
三、额外排查点
如果还是不行,检查这几个常见坑:
- 确保测试设备/模拟器上已正确配置App关联(
assetlinks.json已上传到域名根目录,Firebase控制台已配置对应链接) - 检查
AndroidManifest.xml里的Intent过滤器是否正确匹配动态链接域名:
<intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:host="your-dynamic-link.page.link" android:scheme="https" /> </intent-filter>
- 模拟器上可能需要禁用Chrome的“直接打开链接”选项,确保弹出“打开应用”的选择框
内容的提问来源于stack exchange,提问作者MiguelSlv
相关产品推荐
相关产品推荐

