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注解创建,比较简单

@MockList mockedList;  @Testpublic 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"); 无此操作,验证 failedverify(mockedList).clear();  

做一些桩测试

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

//测试桩,在调用get(0)时返回"first"when(mockedList.get(0)).thenReturn("first");//调用get(1)时抛出异常when(mockedList.get(1)).thenThrow(new RuntimeException());//输出firstSystem.out.println(mockedList.get(0));//抛出异常//System.out.println(mockedList.get(1));//因为get(999)没有打桩,因此输出nullSystem.out.println(mockedList.get(999));  

参数匹配器

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

//使用内置的anyInt()参数匹配器,当调用get(int)时都返回"element"when(mockedList.get(anyInt())).thenReturn("element");//使用自定义的参数器(在inValid()函数中返回你自己的匹配器实现)//when(mockedList.get(isValid())).thenReturn("element");//输出elementSystem.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()/atMostverify(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() {    @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开发进阶 从小工到专家》

相关源码

更多相关文章

  1. Android修改自己程序字体的方法详解
  2. android使用android:ellipsize="end"无效的解决方法
  3. Android 软件安装方法介绍
  4. Android单元测试初探Instrumentation
  5. Android实现全屏显示的方法
  6. Android渗透测试Android渗透测试入门教程大学霸
  7. Android单元测试

随机推荐

  1. Java多线程聊天对话框
  2. 1.4.Java循环结构和break,random()
  3. Java 集合类实现原理
  4. 使用JNI的步骤
  5. javascript实现拖动层效果代码(许愿墙)
  6. 数字金额转换成汉字
  7. java关键字个人理解
  8. 我无法让这个简单的ajax代码工作
  9. Java类的初始化顺序
  10. 向ES6看齐,用更好的JavaScript(三)