Android 单元测试之JUnit和Mockito

Android 单元测试之JUnit和Mockito

Android 单元测试之JUnit和Mockito
Android 单元测试之Roboletric 环境配置
Android 单元测试之Roboletric的简单使用
Android 单元测试之Roboletric RxJava、Retrofit、访问真实网络、虚拟服务器
Android 单元测试之Espresso - Google官方UI测试框架

JUnit

使用JUnit测试框架需要引入依赖,在新建项目的时候,Android Studio已自动帮我们引入了该依赖

dependencies {
    ...
    testCompile 'junit:junit:4.12'  
}

注解说明

JUnit提供了一些注解帮助我们更好的完成测试

@Before
该方法在每次测试方法调用前都会调用
@Test
说明了该方法需要测试
@BeforeClass
该方法在所有测试方法之前调用,只会被调用一次
@After
该方法在每次测试方法调用后都会调用
@AfterClass
该方法在所有测试方法之后调用,只会被调用一次
@Ignore
忽略该方法  

假如我们已经写好了多个测试用例,每次只需要测试其中的三四个,而不需要把所有的测试用例都跑一遍,这该怎么办呢?

不用担心,JUnit已经为我们提供了方法。假设有三个写好的测试用例,我们只需要调用其中的两个,就可以这么做:

@Suite.SuiteClasses({FirstTest.class,ThirdTest.class})
public class SpecializeTests{

}

Mockito

mock对象就是在调试期间用来作为真实对象的替代品。Mockito是Java中常见的Mock框架。

添加依赖

dependencies {
    ...
    testCompile 'org.mockito:mockito-all:2.0.2-beta'
}

创建Mock对象

List mockedList = mock(List.class);  

可用@Mock注解创建,比较简单

@Mock
List mockedList;  

@Test
public void testMock(){
    //初始化@Mock注解的对象 (进行注入)
    MockitoAnnotations.initMocks(this);
}  

使用@Mock注解需要使用MockitoAnnotations.initMocks(this);进行注入

验证某些行为

Mock对象将记住所有的操作,可以验证其行为

//使用Mock对象
mockedList.add("one");
mockedList.clear();

//验证函数的调用次数
verify(mockedList).add("one");
//verify(mockedList).add("two"); 无此操作,验证 failed
verify(mockedList).clear();  

做一些桩测试

在创建出Mock对象后,默认返回值为null,故可做进行打桩,每当调用该打桩后的方法或变量后,让其返回相应的值。

//测试桩,在调用get(0)时返回"first"
when(mockedList.get(0)).thenReturn("first");
//调用get(1)时抛出异常
when(mockedList.get(1)).thenThrow(new RuntimeException());

//输出first
System.out.println(mockedList.get(0));
//抛出异常
//System.out.println(mockedList.get(1));
//因为get(999)没有打桩,因此输出null
System.out.println(mockedList.get(999));  

参数匹配器

让打桩更具灵活性,比如anyInt()将匹配所有的int值

//使用内置的anyInt()参数匹配器,当调用get(int)时都返回"element"
when(mockedList.get(anyInt())).thenReturn("element");
//使用自定义的参数器(在inValid()函数中返回你自己的匹配器实现)
//when(mockedList.get(isValid())).thenReturn("element");

//输出element
System.out.println(mockedList.get(999));
//也可以验证匹配器
//verify(mockedList).get(anyInt());  

验证函数的确切调用次数、最少调用、从未调用

mockedList.add("once");

mockedList.add("twice");
mockedList.add("twice");

mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");

//下面两个的验证结果一样,因为verify默认验证的就是times(1)
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");

//验证具体的执行次数
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times");

//使用never验证,never相当于time(0)
verify(mockedList, never()).add("never happened");

//使用atLeast()/atMost
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("twice");
verify(mockedList, atMost(5)).add("three times");

List mockTwo = mock(List.class);  
//验证Mock对象没有交互过
//verifyZeroInteractions(mockedList); //mockedList已交互过
verifyZeroInteractions(mockTwo); //mockTwo没有交互过

为连续的调用做测试桩(Stub)

打桩根据调用顺序返回不同的值

/*when(mockedList.get(anyInt()))
            .thenThrow(new RuntimeException())
            .thenReturn("foo");*/

/*//第一次调用:抛出运行时异常
System.out.println(mockedList.get(0));
//第二次调用:输出"foo"
System.out.println(mockedList.get(1));
//第三次调用:也是输出"foo"
System.out.println(mockedList.get(2));*/

//另外,连续调用的另一种更简短的方式
when(mockedList.get(anyInt()))
        .thenReturn("one", "two", "three");  

为回调做测试桩

when(mockedList.get(anyInt())).thenAnswer(new Answer<String>() {
    @Override
    public String answer(InvocationOnMock invocation) throws Throwable {
        //获取函数调用的参数
        Object[] args = invocation.getArguments();
        //获得Mock对象本身
        Object mock = invocation.getMock();
        return "answer===>" + mock.toString();
    }
});

System.out.println(mockedList.get(50));

//doReturn(),doThrow(),doAnswer(),doNothing(),noCallRealMethod()  

Spy

Spy可用来处理遗留代码

//spy应尽量少用,可用来处理遗留代码 (没有使用mock生成的对象)
List list = new LinkedList();

//监控一个真实的对象
List spy = spy(list);
//可以为某些函数打桩
when(spy.size()).thenReturn(100);

//在监控真实对象上使用when会报错,可以使用onReturn、Answer、Throw()函数族来进行打桩
//不能:因为当调用spy.get(0)时会调用真实对象的get(0)函数,此时会发生IndexOutOfBoundsException异常,因为真实List对象是空的
//when(spy.get(0)).thenReturn("foo");
doReturn("foo").when(spy).get(0);

System.out.println(spy.get(0));

//Mockito并不会为真实对象代理函数(Method)调用,实际上它会复制真实对象。
//当你在监控一个真实对象时,你想为这个真实对象的函数做测试桩,那么就是在自找麻烦。

//通过spy对象调用真实对象的函数
spy.add("one");
spy.add("two");

System.out.println(spy.get(0));
System.out.println(spy.size());

//交互验证
verify(spy).add("one");
verify(spy).add("two");

为下一步的断言捕获参数

ArgumentCaptor与自定义的参数匹配器相关
这两种技术都能用于检测外出传递到Mock对象的参数
rgumentCaptor更适合以下情况:

1.自定义不能被重用的参数匹配器
2.仅需要断言参数值

//在某些场景中,不光要对方法的返回值和调用进行验证,同时需要验证一系列交互后所传入方法的参数。那么我们可以用参数捕获器来捕获传入方法的参数进行验证,看它是否符合我们的要求。
mockedList.add("Haha");
ArgumentCaptor argument = ArgumentCaptor.forClass(String.class);
verify(mockedList).add(argument.capture());
assertEquals("Haha", argument.getValue());  

ArgumentCaptor详情介绍

其他

参考 《Android开发进阶 从小工到专家》

相关源码

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

氦客

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值