第一个启动测试:使用 ActivityController.launchActivity() 验证生命周期

写 Robolectric 测试,第一步就是让 Activity 跑起来。

你可能会问:直接用 Robolectric.buildActivity() 不就行了?

没错,但背后的 ActivityController 才是真正的核心。它控制着 Activity 的整个生命周期,从创建到销毁,每一步都可以掐着点验证。我个人习惯把 ActivityController 理解为「Activity 遥控器」——想让它走哪一步,按一下对应按钮就行。

为什么不用传统方式启动?

传统 Android 测试里,启动 Activity 依赖 Instrumentation 和 ActivityManager,慢且容易耦合系统服务。

Robolectric 的方案很简单:

  • 用 ActivityController 直接创建 Activity 实例
  • 模拟系统发送生命周期回调
  • 不碰真实的进程和窗口

说白了,它就是个轻量级的 Activity 沙盒。我在项目中遇到过好几次因为 getIntent() 没 mock 导致测试崩掉的场景,用 launchActivity() 配合 Intent 工厂方法,反而能跑得又快又稳。

我的经验: 如果你只是想验证 Activity 能否正常创建并走完 onCreate,直接用 launchActivity() 就够了。真的不需要手动调 setup()、create() 再 start()——那是给特殊场景准备的。

写第一个测试:一分钟上手

来看个最典型的例子。假设你有一个 MainActivity,我们想验证它在启动后 onCreate 被调用了,并且标题设置正确。

@RunWith(RobolectricTestRunner.class)
public class MainActivityTest {

    @Test
    public void shouldLaunchAndVerifyLifecycle() {
        // 创建 Activity 并走到 onResume
        ActivityController<MainActivity> controller =
                Robolectric.buildActivity(MainActivity.class);
        MainActivity activity = controller
                .create()
                .start()
                .resume()
                .get();

        // 验证生命周期标志
        ShadowActivity shadowActivity = Shadows.shadowOf(activity);
        assertThat(shadowActivity.isCreated()).isTrue();
        assertThat(shadowActivity.isResumed()).isTrue();

        // 验证业务逻辑
        assertThat(activity.getTitle()).isEqualTo("Robolectric Demo");
    }
}

你可能会觉得:就这?嗯,就这。但这里面有几个关键点我要拎出来说说。

  • 链式调用顺序: create() → start() → resume() 模拟了 Activity 的完整启动路径。少一个,生命周期就不完整。
  • .get() 取 Activity 对象: 返回真实的 Activity 实例,不是代理。你可以直接调用它的 public 方法。
  • ShadowActivity: 通过 Shadows.shadowOf(activity) 获取代理影子对象,用来检查系统内部状态,比如 isCreated()、isResumed()。
注意: 不要混淆 shadowOf(activity) 和 activity 本身。Shadow 对象只能访问 Robolectric 提供的模拟状态,而 Activity 对象用来执行业务逻辑。我曾经因为用 shadow 去调 Activity 的私有方法而踩坑——记住,shadow 只管系统级状态,不管业务。

验证更多生命周期回调

上面的例子只验证了 onCreate 和 onResume。但你想想看,实际场景中我们经常需要知道 onPause、onStop 甚至 onDestroy 是否被正确调用。

Robolectric 提供了对应的 Controller 方法和 Shadow 检查,我把常用的一一列在下面:

生命周期阶段 Controller 方法 Shadow 验证方法
onCreate .create() shadowActivity.isCreated()
onStart .start() shadowActivity.isStarted()
onResume .resume() shadowActivity.isResumed()
onPause .pause() shadowActivity.isPaused()
onStop .stop() shadowActivity.isStopped()
onDestroy .destroy() shadowActivity.isDestroyed()

为什么要分开验证?因为很多时候,Activity 在 onResume 之后可能被其他页面覆盖(比如按了 Home 键),这时 onPause 和 onStop 会被触发。如果测试没覆盖这些路径,线上很可能出现「杀后台」后的崩溃——我吃过这个亏。

流程回顾:从测试代码到生命周期回调

ActivityController.launchActivity() 执行流程 编写测试类 launchActivity .create() .start() .resume() 断言生命周期状态 @Test onCreate 已调用 onStart 已调用 onResume 已调用 核心思路 用 Controller 模拟系统行为 用 Shadow 验证内部状态

你可能忽略的细节

测试代码看起来简单,但实际工作里有几个坑我不得不提:

  • 别忘了加 @RunWith: 没加的话 Robolectric 不会初始化,测试直接报 ClassNotFoundException。我曾经在迁移到 JUnit5 时忘了加,排查了半小时。
  • 生命周期链式调用不可缺: 如果你只调了 create() 就取 Activity,此时 onResume 还没走,很多和 UI 相关的逻辑会失效。我之前有个测试,绑定了 onResume 时的数据加载,结果只调 create() 怎么都测不出数据——后来补上 .resume() 就好了。
  • Intent 传递: 如果 Activity 依赖 getIntent().getExtras(),记得在 buildActivity() 时传入 Intent:Robolectric.buildActivity(MainActivity.class, new Intent().putExtra("key", "value"))。
一句话总结: ActivityController.launchActivity() 是 Robolectric 测试的基石。学会用链式调用控制生命周期,再用 Shadow 对象验证状态,你已经能覆盖 80% 的启动场景了。

当然,光会启动还远远不够。真实项目里 Activity 会依赖 Fragment、ViewModel、依赖注入框架等等。不过别急,我们先把启动测试这块地基打牢——后面几章会一步步把复杂度加上去。