在实际的单元测试中,我们测试的类之间会有或多或少的耦合,导致我们无法顺利的进行测试,这时我们就可以使用Mockito,Mockito库能够Mock(我喜欢理解为模拟)对象,替换我们原先依赖的真实对象,这样我们就可以避免外部的影响,只测试本类,得到更准确的结果。当然它的功能不仅仅只是这些,例如TDD(测试驱动开发)也是一大亮点,下面我在详细说明。
使用时在build文件中添加依赖。
dependencies { testCompile "org.mockito:mockito-core:2.11.0" }其中后两种方法是结合JUnit框架去实现的,JUnit具体可以参看我的这篇:JUnit框架的使用。四种方法根据个人习惯使用,我个人推荐使用最后一种。
因为Mock出的对象中非void方法都将返回默认值,比如int方法将返回0,对象方法将返回null等,而void方法将什么都不做。“打桩”顾名思义就是将我们Mock出的对象进行操作,比如提供模拟的返回值等,给Mock打基础。
方法名方法描述thenReturn(T value)设置要返回的值thenThrow(Throwable… throwables)设置要抛出的异常thenAnswer(Answer<?> answer)对结果进行拦截doReturn(Object toBeReturned)提前设置要返回的值doThrow(Throwable… toBeThrown)提前设置要抛出的异常doAnswer(Answer answer)提前对结果进行拦截doCallRealMethod()调用某一个方法的真实实现doNothing()设置void方法什么也不做上图是个简单的例子,代码语义很明确,当执行什么方法时,然后就返回什么结果。当然如果我们不打桩,打印结果就是null和0了。
doReturn("小小").when(mPerson).getName(); // 输出"小小" System.out.println(mPerson.getName());如果使用doReturn等打桩方法时,语义为:以什么结果返回,当执行什么方法时。这个两者的区别就是我们熟悉的while与do while。这类方法主要是为了应对无法使用thenReturn等方法的场景(比如方法为void),可读性来说thenReturn这类更好。
thenAnswer方法使用例子:
public String eat(String food){ return food; }eat方法很简单,吃什么吐什么。。。我们用thenAnswer拿到了吃进去的东西,将返回结果重新进行处理。
前面所说的都是状态测试,但是如果不关心返回结果,而是关心方法有否被正确的参数调用过,这时候就应该使用验证方法了。从概念上讲,就是和状态测试所不同的“行为测试”了。
verify(T mock)验证发生的某些行为 。
方法名方法描述after(long millis)在给定的时间后进行验证timeout(long millis)验证方法执行是否超时atLeast(int minNumberOfInvocations)至少进行n次验证atMost(int maxNumberOfInvocations)至多进行n次验证description(String description)验证失败时输出的内容times(int wantedNumberOfInvocations)验证调用方法的次数never()验证交互没有发生,相当于times(0)only()验证方法只被调用一次,相当于times(1)使用代码如下:
@Test public void testPersonVerifyAfter(){ when(mPerson.getAge()).thenReturn(18); //延时1s验证 System.out.println(mPerson.getAge()); System.out.println(System.currentTimeMillis()); verify(mPerson, after(1000)).getAge(); System.out.println(System.currentTimeMillis()); verify(mPerson, atLeast(2)).getAge(); } @Test public void testPersonVerifyAtLeast(){ mPerson.getAge(); mPerson.getAge(); //至少验证2次 verify(mPerson, atLeast(2)).getAge(); } @Test public void testPersonVerifyAtMost(){ mPerson.getAge(); //至多验证2次 verify(mPerson, atMost(2)).getAge(); } @Test public void testPersonVerifyTimes(){ mPerson.getAge(); mPerson.getAge(); //验证方法调用2次 verify(mPerson, times(2)).getAge(); } @Test public void testPersonVerifyTimes(){ mPerson.getAge(); mPerson.getAge(); //验证方法在100ms超时前调用2次 verify(mPerson, timeout(100).times(2)).getAge(); }示例代码:
@Test public void testPersonAny(){ when(mPerson.eat(any(String.class))).thenReturn("米饭"); //或 //when(mPerson.eat(anyString())).thenReturn("米饭"); //输出米饭 System.out.println(mPerson.eat("面条")); } @Test public void testPersonContains(){ when(mPerson.eat(contains("面"))).thenReturn("面条"); //输出面条 System.out.println(mPerson.eat("面")); } @Test public void testPersonArgThat(){ //自定义输入字符长度为偶数时,输出面条。 when(mPerson.eat(argThat(new ArgumentMatcher<String>() { @Override public boolean matches(String argument) { return argument.length() % 2 == 0; } }))).thenReturn("面条"); //输出面条 System.out.println(mPerson.eat("1234")); }1.Spy的创建与mock一样,使用例子如下:
public class MockitoSpyTest { @Spy Person mPerson; @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Test public void testIsNotNull(){ assertNotNull(mPerson); } @Test public void testPersonSpy(){ //输出11 System.out.print(mPerson.getAge()); } }2.inOrder使用代码及测试结果:
3.@InjectMocks: 创建一个实例,这个实例需要的参数用@Mock(或@Spy)注解创建的注入到该实例中。
public class Home { private Person mPerson; public Home(Person person) { mPerson = person; } public String getMaster(){ return mPerson.getName(); } }Mockito框架不支持mock匿名类、final类、static方法、private方法。而PowerMock框架解决了这些问题。关于PowerMock,下一篇会讲到。本篇所有代码已上传至Github。希望大家多多点赞支持!
Mockito 中文文档
JUnit + Mockito 单元测试