一般开发的过程中都需要对业务逻辑进行测试,在 Android 单元测试库中一般会用到 junit,mockito 和 robolectric 这几个库。

Junit

最基本的单元测试库就是 junit,一般用它来测试业务层逻辑和数据层逻辑。
目前在 Android 中使用的是 junit4,在模块中 build.gradle 中添加依赖:

testImplementation 'junit:junit:4.12'
复制代码

下面就可以使用 junit 了。junit 的用法比较简单,在一个方法前加上 @Test 注解就行。方法名虽然可以随便写,但是一般都会以 test 开头,后面加上想要测试的方法名。
在测试过程中一般用的比较多是 Assert,即断言。我们一般用它来判断数据是否正确。

@Test
public void testAssert() {
    Assert.assertEquals(2, 1 + 1);
    Assert.assertThat(1 + 1, CoreMatchers.is(2));
}
复制代码

assertEquals 用来判断后者的表达式结果是否等于前者。assertThat 功能更加强大,而且可读性更好,判断前者的表达式是否匹配后者 matcher。上面的用例用到了 is 这个 matcher,相当于等于的意思。
除了 Assert 这个功能,还有一个比较常用的类 Rule。Rule 用来封装一些有用的规则,扩展现有的功能。一般用 @Rule 注解来初始化一个对象,然后在测试方法中使用这个实例。

@Rule
public ExpectedException exception = ExpectedException.none();
	
@Test
public void testRule() {
    exception.expect(IndexOutOfBoundsException.class);
    throw new IndexOutOfBoundsException();
}
复制代码

上面的示例先初始化一个 ExpectedException 实例,然后使用 expect 方法判断抛出的异常是否是 IndexOutOfBoundsException。

Mockito

虽然 junit 可以测试一些用例,但是我们经常需要模拟真实的数据,这时就要用到 mockito 了。Mockito 是一个模拟库,用来模拟数据。在 Android studio 中使用它只要添加以下的依赖:

testImplementation 'org.mockito:mockito-core:2.13.0'
复制代码

Mockito 中 mock 是一个很重要的概念,用来模拟纯假数据,我们有两种方式可以创建一个 mock。第一种使用Mockito.mock(),第二种使用 @Mock。很显然,使用第二种注解的方式简单很多,但是需要注意的是第二种还需要初始化注解,可以使用以下三种方式来初始化,一种是添加类注解 @RunWith(MockitoJUnitRunner.class),一种是添加 @Rule 注解:

@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.WARN);
复制代码

最后一种是在测试方法调用之前初始化注解:

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
}
复制代码

上例中的 @Before 会在每个 @Test 测试方法调用之前被调用。
除了 mock,spy 也是一个比较重要的概念。相比 mock 这种纯假数据,spy 是半假数据。之所以这么说,是因为是 mock 产生的对象不会真实对象的数据,而 spy 则会影响真实数据。下面可以通过一组测试方法对比下:

@FixMethodOrder(value = MethodSorters.NAME_ASCENDING)
public class MockitoTest {
	
    @Test
    public void testMockObjects() {
        List<String> list = Mockito.mock(List.class);
        System.out.println(list.size());
        list.add("hello");
        System.out.println(list.size());
    }

    @Test
    public void testSpyObjects() {
        List<String> list = new LinkedList<>();
        List<String> spyList = Mockito.spy(list);
        System.out.println(spyList.size());
        spyList.add("hello");
        System.out.println(spyList.size());
    }	
}
复制代码

输出的结果是 0 0 0 1。从结果可以看出 mock 出来的对象不会影响真实对象的数据,但是 spy 出来的对象却会产生影响。这是 mock 和 spy 最大的不同之处。

Robolectric

虽然我们可以模拟出数据,但是 Android 中经常会用到 context 以及四大组件,这个时候 Mockito 就没法模拟了。这个时候就需要用到另一个强大的 Android 单元测试库 Robolectric。Robolectric 这个库主要构建 context 和一些组件,也会 shadow 各种 view,方便我们进行单元测试。但是需要主要的是 Robolectric 是一个单元测试库,只会运行在 PC 上,并不会运行在模拟器或者 Android 设备上。
在 Android studio 中添加依赖:

testImplementation "org.robolectric:robolectric:3.6.1"
复制代码

添加完依赖之后,我们需要添加类注解才能使用 robolectric。

@RunWith(RobolectricTestRunner.class)
复制代码

然后我们就可以构建用例来测试:

@Test
public void startShareActivity_correctIntent() {
    MainActivity activity = Robolectric.setupActivity(MainActivity.class);
    activity.findViewById(R.id.btn_share).performClick();

    Intent expectedIntent = new Intent(activity, ShareActivity.class);
    Intent actualIntent = ShadowApplication.getInstance().getNextStartedActivity();
    Assert.assertEquals(expectedIntent.getComponent(), actualIntent.getComponent());
}
复制代码

这里我们用 Robolectric 来构建一个 activity,然后创建一个期望中的 intent,接着用 ShadowApplication 来创建一个实际的 intent 进行对比。由于 Robolectric 只是一个单元测试库,无法跑在模拟器或者真机上,所以只能通过这种比较 intent 的方式来测试。
总结下,今天我们简述了 Android 单元测试中经常需要用到三个库。junit 主要用来测试一些简单的用例,mockito 用来模拟数据,而 robolectric 主要获取 android 中的 context 和组件进行测试。