android 单元测试
单元测试的重要的和必要性这里不在强调,本文介绍几个常用的单元测试的框架
JUnit4
JUnit4是一个java的单元测试框架,多数Java的开发环境都已经集成了JUnit作为单元测试的工具,android studio默认继承了JUNit4
testImplementation 'junit:junit:4.12'
JUnit4主要是通过注解的方式来识别测试方法,下面介绍几个常用的方法以及它的作用:
- @BeforeClass 在执行所有测试方法之前执行,只执行一次,对应方法必须是静态方法
- @Before 在任意使用@Test注解标注方法执行之前执行
- @Test 测试方法
- @After 在任意使用@Test注解标注方法执行之后执行
- @AfterClass 在执行所有测试方法之后执行,只执行一次,对应方法必须是静态方法
了解完以上的信息还没有完结,单元测试还有很重要的一点验证结果,一般是通过一些assert方法来完成的,JUnit为我们提供的assert方法,多数都在Assert这个类里面
- Assert.assertEquals 验证执行结果与期盼结果比较,这个方法的参数有很多种,根据需要使用
- Assert.assertFalse 验证参数为false
- Assert.assertNotNull 验证参数不为空
- Assert.assertNotSame 参数中的两个对象不是同一个对象
- Assert.assertNull 验证参数为空
- Assert.assertSame 参数中的两个对象是同一个对象
- Assert.assertThat 实际情况满足匹配器给出的条件
- Assert.assertTrue 验证参数为true
由于很多地方用到了Assert.assertThat方法,下面列一下啊Matcher常用的方法
- Matcher.allOf
- Matcher.anyOf
- Matcher.both
- Matcher.containsString
- Matcher.describedAs
- Matcher.either
- Matcher.endsWith
- Matcher.equalTo
- Matcher.instanceOf
- Matcher.is
- Matcher.hasItem
JUnit单元测试时是不能使用Android相关的类的,下面就来认识一下Robolectric框架
Robolectric
Robolectric框架是针对android的测试框架,使用Robolectric框架进行单元测试不用依赖真机或者模拟器,Robolectric支持单元测试范围从Activity的跳转、Activity展示View(包括菜单)和Fragment到View的点击触摸以及事件响应,同时Robolectric也能测试Toast和Dialog。对于需要网络请求数据的测试,Robolectric可以模拟网络请求的response。对于一些Robolectric不能测试的对象,比如ConcurrentTask,可以通过自定义Shadow的方式现实测试。下面将着重介绍Robolectric的常见用法。
引入
testCompile "org.robolectric:robolectric:3.6.1"
官方给出的demo
<pre>
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class)
public class MyActivityTest {
@Test
public void clickingButton_shouldChangeResultsViewText() throws Exception {
Activity activity = Robolectric.setupActivity(MyActivity.class);
Button button = (Button) activity.findViewById(R.id.press_me_button);
TextView results = (TextView) activity.findViewById(R.id.results_text_view);
button.performClick();
assertThat(results.getText().toString(), equalTo("Testing Android Rocks!"));
}
}
</pre>
@RunWith(RobolectricGradleTestRunner.class)
表示用Robolectric的TestRunner来跑这些test,通过gradle CLI或者Android studio运行为单元测试定制的测试运行程序,有一条件必须配置@Config
@RunWith(RobolectricTestRunner.class)
提供Android运行时的模拟环境
Robolectric框架通过shadow类替换对应的真实方法,最终实现测试脱离底层实现,感兴趣的话,可以自行了解
Mock
对于一些依赖关系复杂的测试对象,可以采用Mock框架解除依赖,常用的有Mockito,mock创建一个类的虚假的对象,用于解耦真实对象所需要的依赖,Mock得到的对象仅仅是具备测试对象的类型,并不是真实的对象,也就是并没有执行过真实对象的逻辑。
引入
repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:2.+" }
Mock的方法使用
// mock creation
List mockedList = mock(List.class);
// using mock object - it does not throw any "unexpected interaction" exception
mockedList.add("one");
mockedList.clear();
// selective, explicit, highly readable verification
verify(mockedList).add("one");
verify(mockedList).clear();
直接通过mock方法创建虚拟对象,也可以使用注解的方式 @Mock 来创建,通过 verify 方法来检查相应方法是否调用
stub形式的使用
// you can mock concrete classes, not only interfaces
LinkedList mockedList = mock(LinkedList.class);
// stubbing appears before the actual execution
when(mockedList.get(0)).thenReturn("first");
// the following prints "first"
System.out.println(mockedList.get(0));
// the following prints "null" because get(999) was not stubbed
System.out.println(mockedList.get(999));
指定关键字 when 返回一个 OngoingStubbing 接口,通过其提供的 thenReturn,thenThrow,thenCallRealMethod 及自定义 thenAnswer 来返回相应的结果
Mock中几个主要的方法
- mock()/@Mock: 创建一个Mock虚拟对象
- spy()/@Spy: 部分mock,真正的方法会被调用,但是仍然需要验证
- verify(): 检测方法是否被调用
通过 times,never,atLeastOnce,atLeast,atMost 这些方法,我们可以对一个方法的调用次数做判断
verify(mockedList, atLeastOnce()).add("three times")
通过doThrow为一个方法的调用添加异常,验证代码对异常的处理
doThrow(new RuntimeException()).when(mockedList).clear();
通过Mockito的inOrder方法验证调用顺序
更多使用方法请参考mock官网
注:final类型、private类型以及静态类型的方法不能mock
总结
JUnit框架主要是用来测试java类的,既能做单元测试也能做集成测试,如果想要调用android的方法需要用到Robolectric框架,这两个框架可以做的事情很多,几乎能满足你所有的需求,但是有的时候测试需要用到虚拟对象,就需要用到Mock框架了。
希望这篇文章,对初次接触单元测试的你有所帮助
参考文献: