第一个启动测试:使用 ActivityController.launchActivity() 验证生命周期
写 Robolectric 测试,第一步就是让 Activity 跑起来。
你可能会问:直接用 Robolectric.buildActivity() 不就行了?
没错,但背后的 ActivityController 才是真正的核心。它控制着 Activity 的整个生命周期,从创建到销毁,每一步都可以掐着点验证。我个人习惯把 ActivityController 理解为「Activity 遥控器」——想让它走哪一步,按一下对应按钮就行。
为什么不用传统方式启动?
传统 Android 测试里,启动 Activity 依赖 Instrumentation 和 ActivityManager,慢且容易耦合系统服务。
Robolectric 的方案很简单:
- 用
ActivityController直接创建 Activity 实例 - 模拟系统发送生命周期回调
- 不碰真实的进程和窗口
说白了,它就是个轻量级的 Activity 沙盒。我在项目中遇到过好几次因为 getIntent() 没 mock 导致测试崩掉的场景,用 launchActivity() 配合 Intent 工厂方法,反而能跑得又快又稳。
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 会被触发。如果测试没覆盖这些路径,线上很可能出现「杀后台」后的崩溃——我吃过这个亏。
流程回顾:从测试代码到生命周期回调
你可能忽略的细节
测试代码看起来简单,但实际工作里有几个坑我不得不提:
- 别忘了加
@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、依赖注入框架等等。不过别急,我们先把启动测试这块地基打牢——后面几章会一步步把复杂度加上去。