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

Android Instrumentation测试失败:OAuth2登录自定义标签页deep link捕获异常

解决ActivityScenario.close()时的NullPointerException问题

问题背景

编写测试用例验证自定义标签页完成OAuth2登录并通过deep link捕获code的流程,预期测试通过但失败,报错日志显示在ActivityScenario.close()时触发NullPointerException,提示当前状态为null,最后阶段是STARTED。尝试手动调用moveToState(Lifecycle.State.RESUMED)后问题仍存在。

错误日志

java.lang.NullPointerException: Current state was null unexpectedly. Last stage = STARTED
at androidx.test.internal.util.Checks.checkNotNull(Checks.java:45)
at androidx.test.core.app.ActivityScenario.moveToState(ActivityScenario.java:652)
at androidx.test.core.app.ActivityScenario.close(ActivityScenario.java:416)
at androidx.test.ext.junit.rules.ActivityScenarioRule.after(ActivityScenarioRule.java:115)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:59)
at androidx.test.rule.GrantPermissionRule$RequestPermissionStatement.evaluate(GrantPermissionRule.java:136)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:67)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:58)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:446)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2361)

问题分析

这个错误的核心是测试结束时,ActivityScenario对应的Activity生命周期状态异常——当通过自定义标签页跳转OAuth登录后,原Activity可能被系统置于后台甚至销毁,导致ActivityScenario内部维护的状态丢失,在测试规则的after阶段执行close()时,无法获取当前状态从而抛出NPE。手动调用moveToState无效,是因为此时Scenario已经无法正常关联到原Activity实例。

解决方案

1. 手动管理ActivityScenario生命周期,替代ActivityScenarioRule

放弃使用ActivityScenarioRule自动管理生命周期,改为手动创建和销毁ActivityScenario,在测试结束前确保Activity处于有效状态:

@Test
void testOAuthLoginWithDeepLink() {
    ActivityScenario<MainActivity> scenario = ActivityScenario.launch(MainActivity.class);
    
    // 执行OAuth登录跳转逻辑
    scenario.onActivity(activity -> {
        activity.startOAuthLogin();
    });
    
    // 模拟deep link回调,等待code捕获完成
    simulateDeepLinkCallback();
    
    // 测试结束前,先将Activity切换到RESUMED状态,再关闭
    scenario.moveToState(Lifecycle.State.RESUMED);
    scenario.close();
}

2. 等待deep link处理完成后再结束测试

使用同步机制(如CountDownLatch)确保deep link的code捕获逻辑完全执行完毕,再进行后续的生命周期操作:

@Test
void testOAuthLoginWithDeepLink() throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(1);
    ActivityScenario<MainActivity> scenario = ActivityScenario.launch(MainActivity.class);
    
    scenario.onActivity(activity -> {
        // 设置code捕获完成后的回调
        activity.setCodeCaptureCallback(() -> {
            latch.countDown();
        });
        activity.startOAuthLogin();
    });
    
    // 等待回调触发,确保deep link处理完成
    latch.await(10, TimeUnit.SECONDS);
    
    // 关闭前恢复状态
    scenario.moveToState(Lifecycle.State.RESUMED);
    scenario.close();
}

3. 禁用Activity的自动销毁

在测试中通过ActivityScenario的moveToState保持Activity处于前台状态,避免系统在跳转时销毁它:

@Test
void testOAuthLoginWithDeepLink() {
    ActivityScenario<MainActivity> scenario = ActivityScenario.launch(MainActivity.class);
    // 保持Activity处于RESUMED状态
    scenario.moveToState(Lifecycle.State.RESUMED);
    
    // 执行登录跳转和deep link逻辑
    // ...
    
    // 测试结束正常关闭
    scenario.close();
}

4. 检查测试中的Intent处理逻辑

确保deep link的Intent不会导致原Activity被重新创建(比如避免在Manifest中设置launchMode="singleInstance"等可能导致Activity实例重建的配置,或者在测试中处理好Intent的传递)。

内容的提问来源于stack exchange,提问作者microVinchi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 07:12:53