Android——单元测试

一、作用

基本单元写对了,才有信心继续往下写代码呀,否则不知对错,写了一堆代码出来,那种感觉是不是很忐忑.....

二、概念

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单元测试

你可能感兴趣的:(Android——单元测试)