本文介绍了Android应用的常用测试类型
单元测试
单元测试是编写测试代码,用来检测特定的、明确的、细颗粒的功能
单元测试不仅仅用来保证当前代码的正确性,更重要的是用来保证代码修复、改进或重构之后的正确性
一般来说,单元测试任务包括
- 接口功能测试:用来保证接口功能的正确性
局部数据结构测试(不常用):用来保证接口中的数据结构是正确的
- 比如变量有无初始值
- 变量是否溢出
边界条件测试
- 变量没有赋值(即为NULL)
变量是数值(或字符)
- 主要边界:最小值,最大值,无穷大(对于DOUBLE等)
- 溢出边界(期望异常或拒绝服务):最小值-1,最大值+1
- 临近边界:最小值+1,最大值-1
变量是字符串
- 引用“字符变量”的边界
- 空字符串
- 对字符串长度应用“数值变量”的边界
变量是集合
- 空集合
- 对集合的大小应用“数值变量”的边界
- 调整次序:升序、降序
变量有规律:比如对于Math.sqrt,给出n^2^-1和n^2^+1的边界
所有独立执行通路测试:保证每一条代码,每个分支都经过测试,AndroidStudio中集成了
Jacoco
可以做覆盖率统计- 语句覆盖:保证每一个语句都执行到了
- 判定覆盖(分支覆盖):保证每一个分支都执行到
- 条件覆盖:保证每一个条件都覆盖到true和false(即if、while中的条件语句)
- 路径覆盖:保证每一个路径都覆盖到
- 各条错误处理通路测试:保证每一个异常都经过测试
JUnit
JUnit4
通过注解来识别测试方法
@BeforeClass
全局执行一次,第一个运行@Before
测试方法运行前运行@Test
测试方法@After
测试方法运行后运行@AfterClass
全局执行一次,最后一个运行@Ignore
忽略
Mockito
Mock测试是单元测试的重要方法之一,就是对某些不容易构造或难以获取的对象,用一个虚拟的Mock对象创建以便测试的方法
最大的有点是可以解除单元测试的耦合,如果你的代码对另一个类或接口由依赖,能够模拟这些依赖,帮你验证调用的依赖行为
使用一个接口来描述这个对象 在产品代码中实现这个接口,在测试代码中实现这个接口 在被测试代码中只是通过接口来引用对象,所以它不知道这个引用的对象是真实对象,还是Mock对象
Mockito
是很强大的单元测试Mock框架
验证行为
1 | import static org.mockito.Mockito.*; |
它会记得所有的交互,你可以验证
打桩
1 | LinkedList mockedList = mock(LinkedList.class); |
可以为模拟的接口打桩,指定其行为,上面取第一个元素就会返回first
,取第二个元素会抛出异常,取没有打桩的元素会返回null
参数匹配器
1 | when(mockedList.get(anyInt())).thenReturn("element"); |
用anyInt()
匹配所有元素,用argThat()
指定自定义参数匹配
1 | verify(mock).someMethod(anyInt(), anyString(), eq("third argument")); |
调用额外的调用数字
1 | mockedList.add("once"); |
处理异常
1 | doThrow(new RuntimeException()).when(mockedList).clear(); |
有序验证
1 | List singleMock = mock(List.class); |
确保不会发生交互
1 | mockOne.add("one"); |
寻找多余调用
1 | mockedList.add("one"); |
一个列子
Person.java
1 | public class Person { |
PersonDAO.java
1 | public interface PersonDAO { |
PersonService
1 | public class PersonService { |
PersonServiceTest.java
1 | public class PersonServiceTest { |
仪器测试
Espresso
测试登录页面跳转首页,判断是否登录成功
1 | (AndroidJUnit4.class) |
不是原生输入法,
typeText()
可能导致输入不完整而测试失败,使用replaceText()
立刻填充或换回原生输入法