转载:http://blog.csdn.net/u0134286/article/details/440958简介
Mockito是⼀个流⾏的Mocking框架。它使⽤起来简单,学习成本很低,⽽且具有⾮常简洁的API,测试代码的可读性很⾼。因此它⼗分受欢迎,⽤户群越来越多,很多的开源的软件也选择了Mockito。
要想了解更多有关Mockito的信息,请访问它的官⽅⽹站:http://mockito.org/Stub 和Mock
在开始使⽤Mockito之前,先简单的了解⼀下Stub和Mock的区别。
Stub对象⽤来提供测试时所需要的测试数据,可以对各种交互设置相应的回应。例如我们可以设置⽅法调⽤的返回值等等。Mockito中when(…).thenReturn(…)这样的语法便是设置⽅法调⽤的返回值。另外也可以设置⽅法在何时调⽤会抛异常等。
Mock对象⽤来验证测试中所依赖对象间的交互是否能够达到预期。Mockito中⽤verify(…).methodXxx(…) 语法来验证 methodXxx ⽅法是否按照预期进⾏了调⽤。
有关stub和mock的详细论述见,Martin Fowler⽂章《Mocks Aren't Stub》http://martinfowler.com/articles/mocksArentStubs.html
在 Mocking 框架中所谓的mock 对象实际上是作为上述的stub 和mock 对象同时使⽤的。因为它既可以设置⽅法调⽤返回值,⼜可以验证⽅法的调⽤。Mockito 的获取Jar 包的获取
可以访问下⾯的链接来下载最新的Jar包,笔者使⽤的当前最新版为:1.8.5http://code.google.com/p/mockito/downloads/listMaven
如果项⽬是通过Maven管理的,需要在项⽬的Pom.xml中增加如下的依赖:
在程序中可以import org.mockito.Mockito;然后调⽤它的static⽅法,或者import static org.mockito.Mockito.*;个⼈倾向于后者,因为这样可以更⽅便些。
⼀个简单的例⼦
import static org.junit.Assert.*;import static org.mockito.Mockito.*;import java.util.Iterator;import org.junit.Test;
/***
* @author Brian Zhao*/
public class SimpleTest {@Test
public void simpleTest(){//arrange
Iterator i=mock(Iterator.class);
when(i.next()).thenReturn(\"Hello\").thenReturn(\"World\");//act
String result=i.next()+\" \"+i.next();//verify
verify(i, times(2)).next();//assert
assertEquals(\"Hello World\}}
在上⾯的例⼦中包含了Mockito的基本功能:创建 Mock 对象
创建Mock对象的语法为,mock(class or interface)。例⼦中创建了Iterator接⼝的mock对象。设置⽅法调⽤的预期返回
通过when(mock.someMethod()).thenReturn(value) 来设定mock对象某个⽅法调⽤时的返回值。例⼦中我们对Iterator接⼝的next()⽅法调⽤进⾏了预期设定,当调⽤next()⽅法时会返回”Hello”,由于连续设定了返回值,因此当第⼆次调⽤时将返回”World”。验证⽅法调⽤
接下来对mock对象的next()⽅法进⾏了⼀系列实际的调⽤。mock对象⼀旦建⽴便会⾃动记录⾃⼰的交互⾏为,所以我们可以有选择的对它的交互⾏为进⾏验证。在Mockito中验证mock对象交互⾏为的⽅法是
verify(mock).someMethod(…)。于是⽤此⽅法验证了next()⽅法调⽤,因为调⽤了两次,所以在verify中我们指定了times参数(times的具体应⽤在后⾯会继续介绍)。最后assert返回值是否和预期⼀样。Mock对象的创建和StubbingMock 对象的创建
mock(Class mock(Class 可以对类和接⼝进⾏mock 对象的创建,创建的时候可以为mock 对象命名,也可以忽略命名参数。为mock 对象命名的好处就是调试的时候会很⽅便,⽐如,我们mock 多个对象,在测试失败的信息中会把有问题的mock 对象打印出来,有了名字我们可以很容易定位和辨认出是哪个mock对象出现的问题。另外它也有,对于final类、匿名类和Java的基本类型是⽆法进⾏mock的。Mock 对象的期望⾏为及返回值设定 我们已经了解到可以通过when(mock.someMethod()).thenReturn(value) 来 设定mock对象的某个⽅法调⽤时的返回值,但它也同样有对于static和final修饰的⽅法是⽆法进⾏设定的。下⾯来详细的介绍⼀下有关⽅法及返回值的设定:⾸先假设我们创建Iterator接⼝的mock对象Iterator when(i.next()).thenReturn(\"Hello\")对⽅法设定返回异常 when(i.next()).thenThrow(new RuntimeException())Mockito⽀持迭代风格的返回值设定第⼀种⽅式 when(i.next()).thenReturn(\"Hello\").thenReturn(\"World\")第⼆种⽅式 when(i.next()).thenReturn(\"Hello\上⾯的设定相当于: when(i.next()).thenReturn(\"Hello\")when(i.next()).thenReturn(\"World\") 第⼀次调⽤i.next()将返回”Hello”,第⼆次的调⽤会返回”World”。Stubbing的另⼀种语法doReturn(Object) 设置返回值doReturn(\"Hello\").when(i).next();迭代风格 doReturn(\"Hello\").doReturn(\"World\").when(i).next(); 返回值的次序为从左⾄右,第⼀次调⽤返回”Hello”,第⼆次返回”World”。doThrow(Throwable) 设置返回异常 doThrow(new RuntimeException()).when(i).next(); 因为这种语法的可读性不如前者,所以能使⽤前者的情况下尽量使⽤前者,当然在后⾯要介绍的Spy除外。对 void ⽅法进⾏⽅法预期设定 void⽅法的模拟不⽀持when(mock.someMethod()).thenReturn(value)这样的语法,只⽀持下⾯的⽅式: doNothing() 模拟不做任何返回(mock对象void⽅法的默认返回)doNothing().when(i).remove();doThrow(Throwable) 模拟返回异常 doThrow(new RuntimeException()).when(i).remove();迭代风格 doNothing().doThrow(new RuntimeException()).when(i).remove(); 第⼀次调⽤remove⽅法什么都不做,第⼆次调⽤抛出RuntimeException异常。Argument Matcher(参数匹配器) Mockito通过equals()⽅法,来对⽅法参数进⾏验证。但有时我们需要更加灵活的参数需求,⽐如,匹配任何的String类型的参数等等。参数匹配器就是⼀个能够满⾜这些需求的⼯具。 Mockito框架中的Matchers 类内建了很多参数匹配器,⽽我们常⽤的Mockito对象便是继承⾃Matchers。这些内建的参数匹配器如,anyInt()匹配任何int类型参数,anyString()匹配任何字符串,anySet()匹配任何Set 等。下⾯通过例⼦来说明如何使⽤内建的参数匹配器: @Test public void argumentMatchersTest(){List when(mock.get(anyInt())).thenReturn(\"Hello\").thenReturn(\"World\"); String result=mock.get(100)+\" \"+mock.get(200);verify(mock,times(2)).get(anyInt());assertEquals(\"Hello World\} Stubbing时使⽤内建参数匹配器 例⼦中,⾸先mock 了List 接⼝,然后⽤迭代的⽅式模拟了get ⽅法的返回值,这⾥⽤了anyInt()参数匹配器来匹配任何的int 类型的参数。所以当第⼀次调⽤get⽅法时输⼊任意参数为100⽅法返回”Hello”,第⼆次调⽤时输⼊任意参数200返回值”World”。Verfiy时使⽤参数匹配器 最后进⾏verfiy 验证的时候也可将参数指定为anyInt()匹配器,那么它将不关⼼调⽤时输⼊的参数的具体参数值。注意事项 如果使⽤了参数匹配器,那么所有的参数需要由匹配器来提供,否则将会报错。假如我们使⽤参数匹配器stubbing 了mock 对象的⽅法,那么在verify 的时候也需要使⽤它。如:@Test public void argumentMatchersTest(){Map mapMock = mock(Map.class); when(mapMock.put(anyInt(), anyString())).thenReturn(\"world\");mapMock.put(1, \"hello\"); verify(mapMock).put(anyInt(), eq(\"hello\"));} 在最后的验证时如果只输⼊字符串”hello”是会报错的,必须使⽤Matchers 类内建的eq⽅法。如果将anyInt()换成1进⾏验证也需要⽤eq(1)。详细的内建参数匹配器请参考: http://docs.mockito.googlecode.com/hg/org/mockito/Matchers.htmlMock对象的⾏为验证 之前介绍了如何设置mock对象预期调⽤的⽅法及返回值。下⾯介绍⽅法调⽤的验证,⽽它关注点则在mock 对象的交互⾏为上,⽐如验证mock 对象的某个⽅法调⽤参数,调⽤次数,顺序等等。下⾯来看例⼦:@Test public void verifyTestTest() {List verify(mock).get(2);verify(mock, never()).get(3);verifyNoMoreInteractions(mock);verifyZeroInteractions(mock2);} 验证的基本⽅法 我们已经熟悉了使⽤verify(mock).someMethod(…)来验证⽅法的调⽤。例⼦中,我们mock 了List 接⼝,然后调⽤了mock 对象的⼀些⽅法。验证是否调⽤了mock.get(2)⽅法可以通过verify(mock).get(2)来进⾏。verify ⽅法的调⽤不关⼼是否模拟了get(2)⽅法的返回值,只关⼼mock 对象后,是否执⾏了mock.get(2),如果没有执⾏,测试⽅法将不会通过。验证未曾执⾏的⽅法 在verify⽅法中可以传⼊never()⽅法参数来确认mock.get(3)⽅法不曾被执⾏过。另外还有很多调⽤次数相关的参数将会在下⾯提到。查询多余的⽅法调⽤ verifyNoMoreInteractions()⽅法可以传⼊多个mock对象作为参数,⽤来验证传⼊的这些mock 对象是否存在没有验证过的调⽤⽅法。本例中传⼊参数mock,测试将不会通过,因为我们只verify了mock对象的get(2)⽅法,没有对get(0)和get(1)进⾏验证。为了增加测试的可维护性,官⽅不推荐我们过于频繁的在每个测试⽅法中都使⽤它,因为它只是测试的⼀个⼯具,只在你认为有必要的时候才⽤。查询没有交互的mock对象 verifyZeroInteractions()也是⼀个测试⼯具,源码和verifyNoMoreInteractions()的实现是⼀样的,为了提⾼逻辑的可读性,所以只不过名字不同。在例⼦中,它的⽬的是⽤来确认mock2对象没有进⾏任何交互,但mock2执⾏了get(0)⽅法,所以这⾥测试会报错。由于它和verifyNoMoreInteractions()⽅法实现的源码都⼀样,因此如果在verifyZeroInteractions(mock2)执⾏之前对mock.get(0)进⾏了验证那么测试将会通过。 对 Mock对象⽅法的调⽤次数、顺序和超时进⾏验证验证⽅法调⽤的次数 如果要验证Mock 对象的某个⽅法调⽤次数,则需给verify ⽅法传⼊相关的验证参数,它的调⽤接⼝是verify(T mock, VerificationMode mode) 。如:verify(mock,times(3)).someMethod(argument) 验证mock 对象 someMethod(argument)⽅法是否调⽤了三次。times(N)参数便是验证调⽤次数的参数,N 代表⽅法调⽤次数。其实verify ⽅法中如果不传调⽤次数的验证参数,它默认传⼊的便是times(1),即验证mock 对象的⽅法是否只被调⽤⼀次,如果有多次调⽤测试⽅法将会失败。 Mockito除了提供times(N)⽅法供我们调⽤外,还提供了很多可选的⽅法:never() 没有被调⽤,相当于times(0)atLeast(N) ⾄少被调⽤N次atLeastOnce() 相当于atLeast(1)atMost(N) 最多被调⽤N次超时验证 Mockito 提供对超时的验证,但是⽬前不⽀持在下⾯提到的顺序验证中使⽤。进⾏超时验证和上述的次数验证⼀样,也要在verify 中进⾏参数的传⼊,参数为timeout(int millis),timeout⽅法中输⼊的是毫秒值。下⾯看例⼦: 验证someMethod()是否能在指定的100毫秒中执⾏完毕verify(mock, timeout(100)).someMethod(); 结果和上⾯的例⼦⼀样,在超时验证的同时可进⾏调⽤次数验证,默认次数为1verify(mock, timeout(100).times(1)).someMethod();在给定的时间内完成执⾏次数 verify(mock, timeout(100).times(2)).someMethod();给定的时间内⾄少执⾏两次 verify(mock, timeout(100).atLeast(2)).someMethod();另外timeout也⽀持⾃定义的验证模式,verify(mock, new Timeout(100, yourOwnVerificationMode)).someMethod();验证⽅法调⽤的顺序 Mockito 同样⽀持对不同Mock 对象不同⽅法的调⽤次序进⾏验证。进⾏次序验证是,我们需要创建InOrder对象来进⾏⽀持。例:创建 mock对象 List firstMock.add(\"was called first\");firstMock.add(\"was called first\");secondMock.add(\"was called second\");secondMock.add(\"was called third\");创建InOrder 对象 inOrder⽅法可以传⼊多个mock对象作为参数,这样便可对这些mock对象的⽅法进⾏调⽤顺序的验证InOrder inOrder = inOrder( secondMock,firstMock );验证⽅法调⽤ 接下来我们要调⽤InOrder对象的verify⽅法对mock⽅法的调⽤顺序进⾏验证。注意,这⾥必须是你对调⽤顺序的预期。 InOrder对象的verify⽅法也⽀持调⽤次数验证,上例中,我们期望firstMock.add(\"was called first\")⽅法先执⾏并执⾏两次,所以进⾏了下⾯的验证inOrder.verify(firstMock,times(2)).add(\"was called first\")。 其次执⾏了secondMock.add(\"was called second\")⽅法,继续验证此⽅法的执⾏inOrder.verify(secondMock).add(\"was called second\")。如果mock⽅法的调⽤顺序和InOrder中verify的顺序不同,那么测试将执⾏失败。InOrder的verifyNoMoreInteractions()⽅法 它⽤于确认上⼀个顺序验证⽅法之后,mock 对象是否还有多余的交互。它和Mockito提供的静态⽅法verifyNoMoreInteractions 不同,InOrder的验证是基于顺序的,另外它只验证创建它时所提供的mock 对象,在本例中只对firstMock 和secondMock有效。例如: inOrder.verify(secondMock).add(\"was called second\");inOrder.verifyNoMoreInteractions(); 在验证secondMock.add(\"was called second\")⽅法之后,加上InOrder的verifyNoMoreInteractions⽅法,表⽰此⽅法调⽤后再没有多余的交互。例⼦中会报错,因为在此⽅法之后还执⾏了secondMock.add(\"was called third\")。 现在将上例改成: inOrder.verify(secondMock).add(\"was called third\");inOrder.verifyNoMoreInteractions(); 测试会恢复为正常,因为在secondMock.add(\"was called third\")之后已经没有多余的⽅法调⽤了。如果这⾥换成Mockito类的verifyNoMoreInteractions⽅法测试还是会报错,它查找的是mock对象中是否存在没有验证的调⽤⽅法,和顺序是⽆关的。Mock对象的重置 Mockito提供了reset(mock1,mock2……)⽅法,⽤来重置mock对象。当mock对象被重置后,它将回到刚创建完的状态,没有任何stubbing和⽅法调⽤。这个特性平时是很少⽤到的,因为我们⼤都为每个test ⽅法创建mock,所以没有必要对它进⾏重置。官⽅提供这个特性的唯⼀⽬的是使得我们能在有容器注⼊的mock对象中⼯作更为⽅便。所以,当决定要使⽤这个⽅法的时候,⾸先应该考虑⼀下我们的测试代码是否简洁和专注,测试⽅法是否已经超长了。Answer接⼝(⽅法预期回调接⼝)的应⽤Answer接⼝说明 对mock对象的⽅法进⾏调⽤预期的设定,可以通过thenReturn()来指定返回值,thenThrow()指定返回时所抛异常,通常来说这两个⽅法⾜以应对⼀般的需求。但有时我们需要⾃定义⽅法执⾏的返回结果,Answer 接⼝就是满⾜这样的需求⽽存在的。另外,创建mock 对象的时候所调⽤的⽅法也可以传⼊Answer 的实例mock(java.lang.Class Answer 接⼝定义了参数为InvocationOnMock 对象的answer ⽅法,利⽤InvocationOnMock提供的⽅法可以获取mock ⽅法的调⽤信息。下⾯是它提供的⽅法: getArguments() 调⽤后会以Object数组的⽅式返回mock⽅法调⽤的参数。getMethod() 返回java.lang.reflect.Method 对象getMock() 返回mock对象 callRealMethod() 真实⽅法调⽤,如果mock的是接⼝它将会抛出异常 通过⼀个例⼦来看⼀下Answer 的使⽤。我们⾃定义CustomAnswer 类,它实现了Answer接⼝,返回值为String类型。 public class CustomAnswer implements Answer Object[] args = invocation.getArguments();Integer num = (Integer)args[0];if( num>3 ){return \"yes\";} else { throw new RuntimeException();}}} 这个返回值是这样的逻辑,如果调⽤mock某个⽅法输⼊的参数⼤于3返回”yes”, 否则抛出异常。Answer接⼝的使⽤应⽤⽅式如下:⾸先对List接⼝进⾏mock List 指定⽅法的返回处理类CustomAnswer,因为参数为4⼤于3所以返回字符串”yes”when(mock.get(4)).thenAnswer(new CustomAnswer());另外⼀种⽅式 doAnswer(new CustomAnswer()).when(mock.get(4)); 对void⽅__________法也可以指定Answer来进⾏返回处理,如:doAnswer(new xxxAnswer()).when(mock).clear(); 当设置了Answer后,指定⽅法的调⽤结果就由我们定义的Answer接⼝来处理了。另外我们也可以使⽤匿名内部类来进⾏应⽤:@Test public void customAnswerTest(){List when(mock.get(4)).thenAnswer(new Answer(){ public String answer(InvocationOnMock invocation) throwsThrowable { Object[] args = invocation.getArguments();Integer num = (Integer)args[0];if( num>3 ){return \"yes\";} else { throw new RuntimeException();}}}); System.out.println(mock.get(4));} ⾃定义参数匹配器 Mockito参数匹配器的实现使⽤了Hamcrest框架(⼀个书写匹配器对象时允许直接定义匹配规则的框架,⽹址:http://code.google.com/p/hamcrest/)。它已经提供了许多规则供我们使⽤, Mockito在此基础上也内建了很规则。但有时我们还是需要更灵活的匹配,所以需要⾃定义参数匹配器。ArgumentMatcher 抽象类 ⾃定义参数匹配器的时候需要继承ArgumentMatcher抽象类,它实现了Hamcrest框架的Matcher接⼝,定义了describeTo⽅法,所以我们只需要实现matches ⽅法在其中定义规则即可。 下⾯⾃定义的参数匹配器是匹配size⼤⼩为2 的List:class IsListOfTwoElements extends ArgumentMatcher @Test public void argumentMatchersTest(){List mock = mock(List.class);when(mock.addAll(argThat(new IsListOfTwoElements()))).thenReturn(true);mock.addAll(Arrays.asList(\"one\ verify(mock).addAll(argThat(new IsListOfTwoElements()));} argThat(Matcher IsListOfTwoElements ⽤来匹配size ⼤⼩为2 的List。因为例⼦中传⼊List的元素为三个,所以测试将失败。 较复杂的参数匹配将会降低测试代码的可读性。有时实现参数对象的equals()⽅法是个不错的选择(Mockito默认使⽤equals()⽅法进⾏参数匹配),它可以使测试代码更为整洁。另外,有些场景使⽤参数捕获器(ArgumentCaptor)要⽐⾃定义参数匹配器更加合适。 利⽤ArgumentCaptor(参数捕获器)捕获⽅法参数进⾏验证 在某些场景中,不光要对⽅法的返回值和调⽤进⾏验证,同时需要验证⼀系列交互后所传⼊⽅法的参数。那么我们可以⽤参数捕获器来捕获传⼊⽅法的参数进⾏验证,看它是否符合我们的要求。ArgumentCaptor 介绍 通过 ArgumentCaptor 对象的forClass(Class argument.capture() 捕获⽅法参数 argument.getValue() 获取⽅法参数值,如果⽅法进⾏了多次调⽤,它将返回最后⼀个参数值 argument.getAllValues() ⽅法进⾏多次调⽤后,返回多个参数值应⽤实例@Test public void argumentCaptorTest() {List mock = mock(List.class);List mock2 = mock(List.class);mock.add(\"John\");mock2.add(\"Brian\");mock2.add(\"Jim\"); ArgumentCaptor argument = ArgumentCaptor.forClass(String.class);verify(mock).add(argument.capture());assertEquals(\"John\verify(mock2, times(2)).add(argument.capture());assertEquals(\"Jim\assertArrayEquals(new Object[]{\"Brian\ } ⾸先构建ArgumentCaptor需要传⼊捕获参数的对象,例⼦中是String。接着要在verify ⽅法的参数中调⽤argument.capture()⽅法来捕获输⼊的参数,之后argument变量中就保存了参数值,可以⽤argument.getValue()获取。当某个对象进⾏了多次调⽤后,如mock2 对象,这时调⽤argument.getValue()获取到的是最后⼀次调⽤的参数。如果要获取所有的参数值可以调⽤argument.getAllValues(),它将返回参数值的List。 在某种程度上参数捕获器和参数匹配器有很⼤的相关性。它们都⽤来确保传⼊mock 对象参数的正确性。然⽽,当⾃定义的参数匹配器的重⽤性较差时,⽤参数捕获器会更合适,只需在最后对参数进⾏验证即可。Spy-对象的监视 Mock 对象只能调⽤stubbed ⽅法,调⽤不了它真实的⽅法。但Mockito 可以监视⼀个真实的对象,这时对它进⾏⽅法调⽤时它将调⽤真实的⽅法,同时也可以stubbing 这个对象的⽅法让它返回我们的期望值。另外不论是否是真实的⽅法调⽤都可以进⾏verify验证。和创建mock对象⼀样,对于final类、匿名类和Java的基本类型是⽆法进⾏spy的。监视对象 监视⼀个对象需要调⽤spy(T object)⽅法,如:List spy = spy(newLinkedList());那么spy变量就在监视LinkedList实例。被监视对象的Stubbing stubbing 被监视对象的⽅法时要慎⽤when(Object),如:List spy = spy(new LinkedList()); //Impossible: real method is called so spy.get(0) throwsIndexOutOfBoundsException (the list is yet empty)when(spy.get(0)).thenReturn(\"foo\");//You have to use doReturn() for stubbingdoReturn(\"foo\").when(spy).get(0); 当调⽤when(spy.get(0)).thenReturn(\"foo\")时,会调⽤真实对象的get(0),由于list是空的所以会抛出IndexOutOfBoundsException 异常,⽤doReturn 可以避免这种情况的发⽣,因为它不会去调⽤get(0)⽅法。下⾯是官⽅⽂档给出的例⼦:@Test public void spyTest2() {List list = new LinkedList();List spy = spy(list); //optionally, you can stub out some methods:when(spy.size()).thenReturn(100);//using the spy calls real methodsspy.add(\"one\");spy.add(\"two\"); //prints \"one\" - the first element of a listSystem.out.println(spy.get(0)); //size() method was stubbed - 100 is printedSystem.out.println(spy.size());//optionally, you can verify verify(spy).add(\"one\");verify(spy).add(\"two\");} RETURNS_SMART_NULLS 和RETURNS_DEEP_STUBSRETURNS_SMART_NULLS RETURNS_SMART_NULLS是实现了Answer接⼝的对象,它是创建mock对象时的⼀个可选参数,mock(Class, Answer)。在创建mock对象时,有的⽅法我们没有进⾏stubbing,所以在调⽤的时候有时会返回Null这样在进⾏处理时就很可能抛出NullPointerException。如果通过RETURNS_SMART_NULLS参数来创建的mock对象在调⽤没有stubbed的⽅法时他将返回SmartNull。例如:返回类型是String 它将返回空字符串””;是int,它将返回0;如果是List,它会返回⼀个空的List。另外,在堆栈中可以看到SmartNull的友好提⽰。@Test public void returnsSmartNullsTest() { List mock = mock(List.class, RETURNS_SMART_NULLS);System.out.println(mock.get(0)); System.out.println(mock.toArray().length);} 由于使⽤了RETURNS_SMART_NULLS 参数来创建mock 对象,所以在执⾏下⾯的操作时将不会抛出NullPointerException 异常,另外堆栈也提⽰了相关的信息“SmartNull returned by unstubbed get() method on mock”。RETURNS_DEEP_STUBS 同上⾯的参数⼀样RETURNS_DEEP_STUBS也是⼀个创建mock对象时的备选参数。例如我们有Account 对象和RailwayTicket 对象,RailwayTicket 是Account 的⼀个属性。 public class Account { private RailwayTicket railwayTicket;public RailwayTicket getRailwayTicket() {return railwayTicket;} public void setRailwayTicket(RailwayTicket railwayTicket) {this.railwayTicket = railwayTicket;}} public class RailwayTicket {private String destination;public String getDestination() {return destination;} public void setDestination(String destination) {this.destination = destination;}} 下⾯通过RETURNS_DEEP_STUBS来创建mock 对象。@Test public void deepstubsTest(){ Account account = mock(Account.class, RETURNS_DEEP_STUBS);when(account.getRailwayTicket().getDestination()).thenReturn(\"Beijing\"); account.getRailwayTicket().getDestination();verify(account.getRailwayTicket()).getDestination();assertEquals(\"Beijing\ account.getRailwayTicket().getDestination());} 上例中,我们只创建了Account 的mock 对象,没有对RailwayTicket 创建mock,因为通过RETURNS_DEEP_STUBS参数程序会⾃动进⾏mock所需要的对象,所以上⾯的例⼦等价于:@Test public void deepstubsTest2(){ Account account = mock(Account.class); RailwayTicket railwayTicket = mock(RailwayTicket.class);when(account.getRailwayTicket()).thenReturn(railwayTicket);when(railwayTicket.getDestination()).thenReturn(\"Beijing\");account.getRailwayTicket().getDestination();verify(account.getRailwayTicket()).getDestination();assertEquals(\"Beijing\ account.getRailwayTicket().getDestination());} 为了代码整洁和确保它的可读性,我们应该少⽤这个特性。Mockito对Annotation的⽀持 Mockito ⽀持对变量进⾏注解,例如将mock 对象设为测试类的属性,然后通过注解的⽅式@Mock 来定义它,这样有利于减少重复代码,增强可读性,易于排查错误等。除了⽀持@Mock,Mockito⽀持的注解还有@Spy(监视真实的对象),@Captor(参数捕获器),@InjectMocks(mock对象⾃动注⼊)。Annotation的初始化 只有Annotation还不够,要让它们⼯作起来还需要进⾏初始化⼯作。初始化的⽅法为:MockitoAnnotations.initMocks(testClass)参数testClass是你所写的测试类。⼀般情况下在Junit4的@Before 定义的⽅法中执⾏初始化⼯作,如下:@Before public void initMocks() { MockitoAnnotations.initMocks(this);} 除了上述的初始化的⽅法外,还可以使⽤Mockito 提供的Junit Runner:MockitoJUnitRunner这样就省略了上⾯的步骤。@RunWith(MockitoJUnit44Runner.class)public class ExampleTest {...} @Mock 注解 使⽤@Mock注解来定义mock对象有如下的优点:1. ⽅便mock对象的创建2. 减少mock对象创建的重复代码3. 提⾼测试代码可读性 4. 变量名字作为mock对象的标⽰,所以易于排错 @Mock注解也⽀持⾃定义name 和answer属性。下⾯是官⽅给出的@Mock使⽤的例⼦: public class ArticleManagerTest extends SampleBaseTestCase {@Mock private ArticleCalculator calculator;@Mock(name = \"dbMock\")private ArticleDatabase database;@Mock(answer = RETURNS_MOCKS)private UserProvider userProvider;private ArticleManager manager;@Before public void setup() { manager = new ArticleManager(userProvider, database,calculator);}} public class SampleBaseTestCase {@Before public void initMocks() { MockitoAnnotations.initMocks(this);}} @Spy 注解 Spy的使⽤⽅法请参阅前⾯的章节,在此不再赘述,下⾯是使⽤⽅法:public class Test{@Spy Foo spyOnFoo = new Foo();@Beforepublic void init(){ MockitoAnnotations.initMocks(this);}...} @Captor 注解 @Captor是参数捕获器的注解,有关⽤法见前章,通过注解的⽅式也可以更便捷的对它进⾏定义。使⽤例⼦如下:public class Test {@Captor ArgumentCaptor public void init() { MockitoAnnotations.initMocks(this);}@Test public void shouldDoSomethingUseful() {// ... verify(mock.doStuff(captor.capture()));assertEquals(\"foo\}} @InjectMocks 注解 通过这个注解,可实现⾃动注⼊mock 对象。当前版本只⽀持setter 的⽅式进⾏注⼊,Mockito ⾸先尝试类型注⼊,如果有多个类型相同的mock 对象,那么它会根据名称进⾏注⼊。当注⼊失败的时候Mockito不会抛出任何异常,所以你可能需要⼿动去验证它的安全性。例: @RunWith(MockitoJUnit44Runner.class)public class ArticleManagerTest {@Mock private ArticleCalculator calculator;@Mock private ArticleDatabase database;@Spy private UserProvider userProvider = new ConsumerUserProvider();@InjectMocks private ArticleManager manager = new ArticleManager();@Test public void shouldDoSomething() {manager.initiateArticle(); verify(database).addListener(any(ArticleListener.class));}} 上例中, ArticleDatabase 是ArticleManager 的⼀个属性, 由于 ArticleManager 是注解@InjectMocks 标注的,所以会根据类型⾃动调⽤它的setter⽅法为它设置ArticleDatabase。 mockito api: ⾸先了解mockito 才能知道powermock,看名字就知道powermock更强,下⾯来介绍 下⾯我将以Power Mock的mockito的版本来讲述如何使⽤Power Mock。测试⽬标类: public class ClassUnderTest { public boolean callArgumentInstance(File file) { return file.exists(); } public boolean callInternalInstance(String path) { File file = new File(path); return file.exists(); } public boolean callFinalMethod(ClassDependency refer) { return refer.isAlive(); } public boolean callSystemFinalMethod(String str) { return str.isEmpty(); } public boolean callStaticMethod() { return ClassDependency.isExist(); } public String callSystemStaticMethod(String str) { return System.getProperty(str); } public boolean callPrivateMethod() { return isExist(); } private boolean isExist() { // do something return false; }} 依赖类: public class ClassDependency { public static boolean isExist() { // do something return false; } public final boolean isAlive() { // do something return false; }} 接下来,对6个测试⽤例进⾏逐个的讲解。 ⾸先需要使⽤@RunWith(PowerMockRunner.class)将测试⽤例的runner改为PowerMockRunner1、testCallArgumentInstance:Mock参数传递的对象 @Test public void testCallArgumentInstance() { File file = PowerMockito.mock(File.class); ClassUnderTest underTest = new ClassUnderTest(); PowerMockito.when(file.exists()).thenReturn(true); Assert.assertTrue(underTest.callArgumentInstance(file)); } 需要mock的对象是由参数传进去的,这是最普通的⼀种mock⽅式,jMock,EasyMock,Mockito都能实现。步骤: a、通过PowerMockito.mock(File.class)创建出⼀个mock对象 b、然后再通过PowerMockito.when(file.exists()).thenReturn(false);来指定这个mock对象具体的⾏为c、再将mock对象作为参数传递个测试⽅法,执⾏测试⽅法。2、testCallInternalInstance:Mock⽅法内部new出来的对象 @Test @PrepareForTest(ClassUnderTest.class) public void testCallInternalInstance() throws Exception { File file = PowerMockito.mock(File.class); ClassUnderTest underTest = new ClassUnderTest(); PowerMockito.whenNew(File.class).withArguments(\"bbb\").thenReturn(file); PowerMockito.when(file.exists()).thenReturn(true); Assert.assertTrue(underTest.callInternalInstance(\"bbb\")); } 需要mock的对象是在⽅法内部new出来的,这是⼀种⽐较常见的mock⽅式。步骤(已经讲过的步骤省略): a、通过PowerMockito.whenNew(File.class).withArguments(\"bbb\").thenReturn(file)来指定当以参数为bbb创建File对象的时候,返回已经mock的File对象。b、在测试⽅法之上加注解@PrepareForTest(ClassUnderTest.class),注解⾥写的类是需要mock的new对象代码所在的类。3、testCallFinalMethod:Mock普通对象的final⽅法。 @Test @PrepareForTest(ClassDependency.class) public void testCallFinalMethod() { ClassDependency depencency = PowerMockito.mock(ClassDependency.class); ClassUnderTest underTest = new ClassUnderTest(); PowerMockito.when(depencency.isAlive()).thenReturn(true); Assert.assertTrue(underTest.callFinalMethod(depencency)); } Mock的步骤和之前的⼀样,只是需要在测试⽅法之上加注解@PrepareForTest(ClassDependency.class),注解⾥写的类是需要mock的final⽅法所在的类。4、testCallStaticMethod:Mock静态⽅法。 @Test @PrepareForTest(ClassDependency.class) public void testCallStaticMethod() { ClassUnderTest underTest = new ClassUnderTest(); PowerMockito.mockStatic(ClassDependency.class); PowerMockito.when(ClassDependency.isExist()).thenReturn(true); Assert.assertTrue(underTest.callStaticMethod()); } 步骤: a、通过PowerMockito.mockStatic(ClassDependency.class);表⽰需要mock这个类⾥的静态⽅法 b、在测试⽅法之上加注解@PrepareForTest(ClassDependency.class),注解⾥写的类是需要mock的静态⽅法所在的类。5、testCallSystemStaticMethod:Mock JDK中类的静态⽅法。 testCallSystemFinalMethod:Mock JDK对象的final⽅法。 @Test @PrepareForTest(ClassUnderTest.class) public void testCallSystemStaticMethod() { ClassUnderTest underTest = new ClassUnderTest(); PowerMockito.mockStatic(System.class); PowerMockito.when(System.getProperty(\"aaa\")).thenReturn(\"bbb\"); Assert.assertEquals(\"bbb\ } @Test @PrepareForTest(ClassUnderTest.class) public void testCallSystemFinalMethod() { String str = PowerMockito.mock(String.class); ClassUnderTest underTest = new ClassUnderTest(); PowerMockito.when(str.isEmpty()).thenReturn(false); Assert.assertFalse(underTest.callJDKFinalMethod(str)); } 和Mock普通对象的静态⽅法、final⽅法⼀样,只不过注解⾥写的类不⼀样@PrepareForTest(ClassUnderTest.class),注解⾥写的类是需要调⽤系统⽅法所在的类。 6、testCallPrivateMethod:Mock私有⽅法。 @Test @PrepareForTest(ClassUnderTest.class) public void testCallPrivateMethod() throws Exception { ClassUnderTest underTest = PowerMockito.mock(ClassUnderTest.class); PowerMockito.when(underTest.callPrivateMethod()).thenCallRealMethod(); PowerMockito.when(underTest, \"isExist\").thenReturn(true); Assert.assertTrue(underTest.callPrivateMethod()); } 和Mock普通⽅法⼀样,只是需要加注解@PrepareForTest(ClassUnderTest.class),注解⾥写的类是私有⽅法所在的类。完整的测试⽤例类: @RunWith(PowerMockRunner.class)public class TestClassUnderTest { @Test public void testCallArgumentInstance() { File file = PowerMockito.mock(File.class); ClassUnderTest underTest = new ClassUnderTest(); PowerMockito.when(file.exists()).thenReturn(true); Assert.assertTrue(underTest.callArgumentInstance(file)); } @Test @PrepareForTest(ClassUnderTest.class) public void testCallInternalInstance() throws Exception { File file = PowerMockito.mock(File.class); ClassUnderTest underTest = new ClassUnderTest(); PowerMockito.whenNew(File.class).withArguments(\"bbb\").thenReturn(file); PowerMockito.when(file.exists()).thenReturn(true); Assert.assertTrue(underTest.callInternalInstance(\"bbb\")); } @Test @PrepareForTest(ClassDependency.class) public void testCallFinalMethod() { ClassDependency depencency = PowerMockito.mock(ClassDependency.class); ClassUnderTest underTest = new ClassUnderTest(); PowerMockito.when(depencency.isAlive()).thenReturn(true); Assert.assertTrue(underTest.callFinalMethod(depencency)); } @Test @PrepareForTest(ClassUnderTest.class) public void testCallSystemFinalMethod() { String str = PowerMockito.mock(String.class); ClassUnderTest underTest = new ClassUnderTest(); PowerMockito.when(str.isEmpty()).thenReturn(false); Assert.assertFalse(underTest.callSystemFinalMethod(str)); } @Test @PrepareForTest(ClassDependency.class) public void testCallStaticMethod() { ClassUnderTest underTest = new ClassUnderTest(); PowerMockito.mockStatic(ClassDependency.class); PowerMockito.when(ClassDependency.isExist()).thenReturn(true); Assert.assertTrue(underTest.callStaticMethod()); } @Test @PrepareForTest(ClassUnderTest.class) public void testCallSystemStaticMethod() { ClassUnderTest underTest = new ClassUnderTest(); PowerMockito.mockStatic(System.class); PowerMockito.when(System.getProperty(\"aaa\")).thenReturn(\"bbb\"); Assert.assertEquals(\"bbb\ } @Test @PrepareForTest(ClassUnderTest.class) public void testCallPrivateMethod() throws Exception { ClassUnderTest underTest = PowerMockito.mock(ClassUnderTest.class); PowerMockito.when(underTest.callPrivateMethod()).thenCallRealMethod(); PowerMockito.when(underTest, \"isExist\").thenReturn(true); Assert.assertTrue(underTest.callPrivateMethod()); }} ------------------------------------------------------------下⾯是我⾃⼰的例⼦mock静态类 private void prepareForGosServiceFactory() { ClassPathXmlApplicationContext ctx=PowerMockito.mock(ClassPathXmlApplicationContext.class); try { PowerMockito.whenNew(ClassPathXmlApplicationContext.class).withAnyArguments().thenReturn(ctx); UpdateSoService updateSoService=PowerMockito.mock(UpdateSoService.class); PowerMockito.when(ctx.getBean(\"wrapUpdateOrderHessianCall\")).thenReturn(updateSoService); PowerMockito.mockStatic(GosServiceFactory.class); PowerMockito.when(GosServiceFactory.getUpdateOrderService()).thenReturn(updateSoService); PowerMockito.when(updateSoService.updateRemarkIdByOrderId(Mockito.any(RemarkInput.class))); } catch (Exception e) { } } @Test public void testDigExperience() { long peid = 1234L; int siteId = 1; String ip = \"127.0.0.1\"; org.mockito.Mockito.when(productExperienceDao.countExperienceDig(peid, ip)).thenReturn(0L);// mock 返回值 org.mockito.Mockito.doNothing().when(productExperienceDao).updateExperienceUpNum(peid,siteId); // mock void⽅法 org.mockito.Mockito.doNothing().when(productExperienceDao).updateExperienceDownNum(peid, siteId); ProductExperienceDig ped = new ProductExperienceDig(); org.mockito.Mockito.when(productExperienceDao.insertPED(ped)).thenReturn(0L); target.digExperience_pe(peid, ip, 1l, \"up\"); target.digExperience_pe(peid, ip, 1l, \"down\"); } //⼀个⽅法重复调⽤多次,返回值不同的情况,可以这样写 org.mockito.Mockito.when(productExperienceDao.getProductExperienceByIdAndProductIdAndSiteType(Mockito.anyLong(),Mockito.anyLong(),Mockito.anyInt())) .thenReturn(experienceOld,experienceOld1,experienceOld2,experienceOld3,experienceOld4); //注意,如果参数有⼀个使⽤了Any类型,那么全部都必须⽤Any类型 org.mockito.Mockito.when(productExperienceDao.queryBoutqueAndTopMostPEByMainProdIds(Mockito.anyList(),Mockito.anyInt(),Mockito.anyInt())).thenReturn(yhdMap); 因篇幅问题不能全部显示,请点此查看更多更全内容 {public boolean matches(Object list) {return ((List) list).size() == 2;}}
Copyright © 2019- niushuan.com 版权所有 赣ICP备2024042780号-2
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务