测试基础:JUnit、Mockito、Robolectric 等工具介绍
开始做 AOSP 测试之前,咱们得先把地基打牢。
我记得刚转做 Android 平台测试时,面对 JUnit、Mockito、Robolectric 这些名字,心里直打鼓。到底先学哪个?它们各自解决什么问题?
说白了,这三兄弟分工非常明确:
- JUnit —— 测试框架的骨架,所有单元测试的基础设施。
- Mockito —— 模拟依赖的利器,帮我们把外部对象“隔离”掉。
- Robolectric —— 在 JVM 上模拟 Android 环境,省去跑模拟器的麻烦。
下面我挨个聊聊,结合我自己踩过的坑,给你讲透它们怎么用。
JUnit —— 测试框架的基石
JUnit 是 Java 生态里最基础的单元测试框架。做 AOSP 测试,你几乎每个测试类都会用到它。
我个人习惯用 JUnit 4 的注解风格,因为 AOSP 很多老模块还是 JUnit 4。当然 JUnit 5 也好,但这里咱们先以 JUnit 4 为例。
核心注解速查表
| 注解 | 作用 | 执行时机 |
|---|---|---|
| @Test | 标记一个测试方法 | 每次运行 |
| @Before | 每个测试前执行,初始化对象 | 每个 @Test 前 |
| @After | 每个测试后执行,清理资源 | 每个 @Test 后 |
| @BeforeClass | 类中所有测试前执行一次(静态方法) | 类加载时 |
| @AfterClass | 类中所有测试后执行一次(静态方法) | 类销毁时 |
| @Ignore | 跳过某个测试 | 不执行 |
举个例子:
public class CalculatorTest {
private Calculator calc;
@Before
public void setUp() {
calc = new Calculator();
}
@Test
public void add_shouldReturnSum() {
int result = calc.add(2, 3);
assertEquals(5, result);
}
@After
public void tearDown() {
calc = null;
}
}
这里 assertEquals 就是 JUnit 提供的断言。断言失败时,测试会红掉。
💡 我的一点经验
JUnit 的 @Before 和 @After 非常适合做资源准备和释放。比如 Android 的 Context 你可以在这里 mock 好再注入。千万别写在 @Test 里重复造轮子。
Mockito —— 模拟外部依赖
测试里最难搞的就是外部依赖:网络请求、数据库、系统服务…… 你不想真的去启动一个 ContentProvider 吧?Mockito 就是解决这个的。
Mockito 能帮我们:
- 创建 Mock 对象:用 mock(Class) 生成一个假对象。
- Stubbing(打桩):指定调用某个方法时返回什么值。
- 验证(Verify):检查某个方法是否被调用了,调了几次。
来看一段代码:
// 创建 mock
Context mockContext = mock(Context.class);
// 打桩:当调用 getString(R.string.app_name) 时返回 "MyApp"
when(mockContext.getString(R.string.app_name)).thenReturn("MyApp");
// 测试代码...
String name = mockContext.getString(R.string.app_name);
assertEquals("MyApp", name);
// 验证:确保 getString 被调用了一次
verify(mockContext, times(1)).getString(R.string.app_name);
这里要注意:如果没打桩的方法被调用了,Mockito 会返回默认值(如 0、false、null)。
⚠️ 曾经踩过的坑
我曾经在一个测试里 mock 了 ContentResolver,但忘了打桩 query() 方法。结果跑测试时一直返回 null,排查了两小时才发现。建议对所有 mock 对象的每个可能被调用的方法都明确打桩,哪怕返回 null 也要写清楚。
Mockito 还有一个常用的功能:@InjectMocks 和 @Mock 注解。配合 MockitoJUnitRunner 或者 MockitoAnnotations.initMocks(this) 使用时,可以自动注入 mock 对象到被测类里。
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
@Mock
private UserDao userDao;
@InjectMocks
private UserService userService;
@Test
public void getUser_shouldReturnUser() {
User expected = new User("1", "Alice");
when(userDao.findById("1")).thenReturn(expected);
User actual = userService.getUser("1");
assertEquals(expected, actual);
}
}
Robolectric —— 在 JVM 上跑 Android 测试
Android 单元测试最让人头疼的是:很多代码依赖 Android 框架类(比如 Activity、Context、Resource)。如果你用 JUnit 直接测,就会抛出 RuntimeException:Stub! —— 因为 Android 的 SDK 在 JVM 上是存根。
Robolectric 就是来拯救我们的。它在 JVM 上实现了 Android 框架的「影子(Shadow)」版本,让你的测试能跑起来,而不需要启动模拟器。
典型用法:
@RunWith(RobolectricTestRunner.class)
public class MainActivityTest {
@Test
public void activity_shouldCreate() {
ActivityController<MainActivity> controller = Robolectric.buildActivity(MainActivity.class);
controller.create().start().resume();
MainActivity activity = controller.get();
assertNotNull(activity);
}
}
Robolectric 还给每个 Android 类配了一个 Shadow 类。比如 ShadowToast 可以检查 Toast 是否被弹出:
@Test
public void buttonClick_shouldShowToast() {
MainActivity activity = Robolectric.setupActivity(MainActivity.class);
activity.findViewById(R.id.btn).performClick();
Toast latestToast = ShadowToast.getLatestToast();
assertNotNull(latestToast);
assertEquals("Hello", ShadowToast.getTextOfLatestToast());
}
🎯 使用建议
Robolectric 虽然强大,但它不是模拟器,有些特性无法模拟(比如真正的传感器、硬件编解码)。我建议用它测 UI 逻辑、资源加载、生命周期,涉及硬件或特殊系统服务时,还是走 Instrumentation 测试。
三剑客的协作模式
实际写 AOSP 测试时,这三者通常一起上。比如你要测试一个 ViewModel,它依赖 SharedPreferences(Android 类)和一个网络数据源:
- 用 Robolectric 提供 Android 环境(让 SharedPreferences 能创建)。
- 用 Mockito 把网络数据源 mock 掉,避免真实网络请求。
- 用 JUnit 的 @Test 和断言来定义测试逻辑。
@RunWith(RobolectricTestRunner.class)
public class MyViewModelTest {
@Mock
private DataSource dataSource;
private MyViewModel viewModel;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
viewModel = new MyViewModel(RuntimeEnvironment.application, dataSource);
}
@Test
public void loadData_shouldPopulateLiveData() {
List<Item> mockData = Arrays.asList(new Item("a"), new Item("b"));
when(dataSource.fetchItems()).thenReturn(Observable.just(mockData));
viewModel.loadData();
assertEquals(2, viewModel.items.getValue().size());
}
}
✅ 总结一下
JUnit 是骨架,Mockito 是隔离针,Robolectric 是环境模拟器。掌握这三样,AOSP 单元测试的基本功就到位了。后面几章咱们会深入到更复杂的场景:PowerMock、参数化测试、还有 AOSP 特有的测试基类。但先把这三个工具练熟,你会发现写测试越来越顺。