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());
其他
参考 《Android开发进阶 从小工到专家》