一、作用
基本单元写对了,才有信心继续往下写代码呀,否则不知对错,写了一堆代码出来,那种感觉是不是很忐忑.....
二、概念
1. 单测要测的是什么
(1)逻辑
(2)性能
2. 单测分类
(1)本地测试(local tests)
在本地机器 JVM 上运行,以最小化执行时间。这种测试不依赖 Android 框架,即使依赖也可以通过通过模拟框架进行模拟,以达到隔离 Android 依赖的目的
(2)仪器测试(instrumented tests)
在真机或模拟机上运行的单测,由于要运行到设备上,所以比较慢。这些测试可以访问仪器(Android 系统)信息。一般地,依赖不太容易通过模拟框架模拟时用这种方式。
3. 测试代码存放位置
AS 已经帮我们创建好了测试代码存储目录
app/src
├── androidTestjava (仪器化单元测试、UI测试)
├── main/java (业务代码)
└── test/java (本地单元测试)
4. Junit
唯鹿-单测
- 注解
- 方法
- 验证是否抛出异常
- 参数化测试
连续用一系列值测试,省的一遍遍修改 - Rule
5. Mokito 用法
通过 Mokito 模拟对象,从而隔离 Android 依赖。由于mockito只在JVM环境生效,而 Android 是运行在Dalvik 或 ART环境,所以 AndroidJunitRunner 不能使用 mockito。
(1)4种 mock 的方式
唯鹿——Mockito
public class MockitoTest{
@Test
public void testIsNotNull(){
// 使用 mock 方法
Person person=mock(Person.class);
assertNotNull(person);
}
}
(2)常用打桩方法
- when-thenReturn
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class MockTest {
public static void main(String[] args) {
IMathUtils mathUtils = mock(IMathUtils.class); // 生成mock对象
when(mathUtils.abs(-1)).thenReturn(1); // 当调用abs(-1)时,返回1
int abs = mathUtils.abs(-1); // 输出结果 1
Assert.assertEquals(abs, 1);// 测试通过
}
}
- doReturn() 和 thenReturn()
(3)常用验证方法
不关心返回结果,而是关心方法是否被正确地参数调用过。
(4)常用参数匹配器
@Test
public void testPersonVerifyAtLeast() {
mPerson.getAge();
mPerson.getAge();
//至少验证2次
verify(mPerson, atLeast(2)).getAge();
}
6. AndroidJunitRunner
AndroidJUnitRunner 是 Google 官方的 Android 单元测试框架之一,使用跟 Junit 是一样的,只不过需要运行在android真机或模拟器环境。由于 mockito 只在 jvm 环境生效,而 android 是运行在 Dalvik 或ART,所以 AndroidJUnitRunner 不能使用 mockito。
三、使用
1. 单测技巧——依赖隔离
避免外部的影响,只测试本类,得到更准确的结果。
Calulator
类中提供了一个方法求商
public class Calculator{
public double divide(int a,int b){
// 检测被除数是否为0
if(MathUtils.checkZero(b)){
throw new RuntimeException("divided is zero");
}
return (double)a/b;
}
}
public class MathUtils{
public static boolean checkZero(int num){
return num==0;
}
}
Calculator::divide()
依赖了MathUtils.checkZero()
,我们对Calculator::divide()
进行测试时,如果MathUtils.checkZero()
里面的逻辑没写对,将对测试造成直接影响。故要依赖隔离。
我们使用 mockito 提供的 mock 提供的功能模拟MathUtils.checkZero()
方法的调用。但 mockito 不支持 mock static、private、final 等方法(PowerMock ,它拓展了Mockito框架,从而支持了mock static方法、private方法、final方法与类等等。)。所以改造代码,在Calculator
的构造函数或setter
中将MathUtils
的实例传入。
public class Calculator{
private MathUtils mathUtils;
public Calculator(MathUtils utils){
this.mathUtils=utils;
}
public double divide(int a,int b){
// 检测被除数是否为0
if(mathUtils.checkZero(b)){
throw new RuntimeException("divided is zero");
}
return (double)a/b;
}
}
public class MathUtils{
....
}
单测示例
@Test
public void test() {
// 生成MathUtils的模拟对象
MathUtils mathUtils = mock(MathUtils.class);
when(mathUtils.checkZero(1)).thenReturn(false);
when(mathUtils.checkZero(0)).thenReturn(true);
Calculator calculator = new Calculator(mathUtils);
Assert.assertEquals(calculator.divide(2, 1), 2);
try {
calculator.divide(2, 0);
throw new RuntimeException("no expectant exception");
} catch (Exception e) {
Assert.assertEquals(e.getMessage(), "divide by zero");
}
}
四、重点了解
1. Android 测试代码位置
测试也是Android开发的重要部分,单元测试和UI测试上手实践
- 本地测试
- 仪器测试
参考文献
Android单元测试只看这一篇就够了
Android测试-必知必会
开始Android单元测试