转载:http://blog.csdn.net/u013428664/article/details/44095889

简介

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.5

http://code.google.com/p/mockito/downloads/list

Maven

如果项目是通过Maven管理的,需要在项目的Pom.xml中增加如下的依赖:

<dependencies>

<dependency>

<groupId>org.mockito</groupId>

<artifactId>mockito-all</artifactId>

<version>1.8.5</version>

<scope>test</scope>

</dependency>

</dependencies>

从一个实例开始

Mocktio包的引入

在程序中可以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", result);

}

}

在上面的例子中包含了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对象的创建和Stubbing

Mock 对象的创建

mock(Class<T> classToMock)

mock(Class<T> classToMock, String name)

可以对类和接口进行mock 对象的创建,创建的时候可以为mock 对象命名,也

可以忽略命名参数。为mock 对象命名的好处就是调试的时候会很方便,比如,

我们mock 多个对象,在测试失败的信息中会把有问题的mock 对象打印出来,

有了名字我们可以很容易定位和辨认出是哪个mock对象出现的问题。另外它也

有限制,对于final类、匿名类和Java的基本类型是无法进行mock的。

Mock 对象的期望行为及返回值设定

我们已经了解到可以通过when(mock.someMethod()).thenReturn(value) 来

设定mock对象的某个方法调用时的返回值,但它也同样有限制对于static和final

修饰的方法是无法进行设定的。下面来详细的介绍一下有关方法及返回值的设定:

首先假设我们创建Iterator接口的mock对象

Iterator<String> i = mock(Iterator.class);

对方法设定返回值

when(i.next()).thenReturn("Hello")

对方法设定返回异常

when(i.next()).thenThrow(new RuntimeException())

Mockito支持迭代风格的返回值设定

第一种方式

when(i.next()).thenReturn("Hello").thenReturn("World")

第二种方式

when(i.next()).thenReturn("Hello", "World")

上面的设定相当于:

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<String> mock = mock(List.class);

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",result);

}

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.html

Mock对象的行为验证

之前介绍了如何设置mock对象预期调用的方法及返回值。下面介绍方法调用的

验证,而它关注点则在mock 对象的交互行为上,比如验证mock 对象的某个方

法调用参数,调用次数,顺序等等。下面来看例子:

@Test

public void verifyTestTest() {

List<String> mock = mock(List.class);

List<String> mock2 = mock(List.class);

when(mock.get(0)).thenReturn("hello");

mock.get(0);

mock.get(1);

mock.get(2);

mock2.get(0);

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();

结果和上面的例子一样,在超时验证的同时可进行调用次数验证,默认次数为1

verify(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<String> firstMock = mock(List.class);

List<String> secondMock = mock(List.class);

调用mock对象方法

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<T> classToMock, Answer defaultAnswer),它可以用来处理那

些mock对象没有stubbing的方法的返回值。

InvocationOnMock 对象的方法

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<String> {

public String answer(InvocationOnMock invocation) throws

Throwable {

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<String> mock = mock(List.class);

指定方法的返回处理类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<String> mock = mock(List.class);

when(mock.get(4)).thenAnswer(new Answer(){

public String answer(InvocationOnMock invocation) throws

Throwable {

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<List> {

public boolean matches(Object list) {

return ((List) list).size() == 2;

}

}

@Test

public void argumentMatchersTest(){

List mock = mock(List.class);

when(mock.addAll(argThat(new

IsListOfTwoElements()))).thenReturn(true);

mock.addAll(Arrays.asList("one", "two", "three"));

verify(mock).addAll(argThat(new IsListOfTwoElements()));

}

argThat(Matcher<T> matcher)方法用来应用自定义的规则,可以传入任何实现

Matcher 接口的实现类。上例中在stubbing 和verify addAll 方法时通过

argThat(Matcher<T> matcher) , 传入了自定义的参数匹配器

IsListOfTwoElements 用来匹配size 大小为2 的List。因为例子中传入List

的元素为三个,所以测试将失败。

较复杂的参数匹配将会降低测试代码的可读性。有时实现参数对象的equals()

方法是个不错的选择(Mockito默认使用equals()方法进行参数匹配),它可以

使测试代码更为整洁。另外,有些场景使用参数捕获器(ArgumentCaptor)要比

自定义参数匹配器更加合适。

利用ArgumentCaptor(参数捕获器)捕获方法参数进行验证

在某些场景中,不光要对方法的返回值和调用进行验证,同时需要验证一系列交

互后所传入方法的参数。那么我们可以用参数捕获器来捕获传入方法的参数进行

验证,看它是否符合我们的要求。

ArgumentCaptor 介绍

通过 ArgumentCaptor 对象的forClass(Class<T> clazz)方法来构建ArgumentCaptor

对象。然后便可在验证时对方法的参数进行捕获,最后验证捕获的参数值。如果

方法有多个参数都要捕获验证,那就需要创建多个ArgumentCaptor对象处理。

ArgumentCaptor的Api

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", argument.getValue());

verify(mock2, times(2)).add(argument.capture());

assertEquals("Jim", argument.getValue());

assertArrayEquals(new

Object[]{"Brian","Jim"},argument.getAllValues().toArray());

}

首先构建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(new

LinkedList());那么spy变量就在监视LinkedList实例。

被监视对象的Stubbing

stubbing 被监视对象的方法时要慎用when(Object),如:

List spy = spy(new LinkedList());

//Impossible: real method is called so spy.get(0) throws

IndexOutOfBoundsException (the list is yet empty)

when(spy.get(0)).thenReturn("foo");

//You have to use doReturn() for stubbing

doReturn("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 methods

spy.add("one");

spy.add("two");

//prints "one" - the first element of a list

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

//size() method was stubbed - 100 is printed

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

//optionally, you can verify

verify(spy).add("one");

verify(spy).add("two");

}

RETURNS_SMART_NULLS 和RETURNS_DEEP_STUBS

RETURNS_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();

@Before

public void init(){

MockitoAnnotations.initMocks(this);

}

...

}

@Captor 注解

@Captor是参数捕获器的注解,有关用法见前章,通过注解的方式也可以更便捷

的对它进行定义。使用例子如下:

public class Test {

@Captor

ArgumentCaptor<AsyncCallback<Foo>> captor;

@Before

public void init() {

MockitoAnnotations.initMocks(this);

}

@Test

public void shouldDoSomethingUseful() {

// ...

verify(mock.doStuff(captor.capture()));

assertEquals("foo", captor.getValue());

}

}

@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:http://docs.mockito.googlecode.com/hg-history/be6d53f62790ac7c9cf07c32485343ce94e1b563/1.9.5/org/mockito/Mockito.html

首先了解mockito 才能知道powermock,看名字就知道powermock更强,下面来介绍

<dependency>
 <groupId>org.powermock</groupId>
 <artifactId>powermock-api-mockito</artifactId>
 <version>1.4.10</version>
 <scope>test</scope>
</dependency>

<dependency>
 <groupId>org.powermock</groupId>
 <artifactId>powermock-module-junit4</artifactId>
 <version>1.4.10</version>
 <scope>test</scope>
</dependency>

下面我将以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改为PowerMockRunner
1、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", underTest.callJDKStaticMethod("aaa"));
} @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", underTest.callSystemStaticMethod("aaa"));
} @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);

使用Powermock和mockito来进行单元测试的更多相关文章

  1. Spring Boot 2 实践记录之 使用 Powermock、Mockito 对 UUID 进行 mock 单元测试

    由于注册时,需要对输入的密码进行加密,使用到了 UUID.sha1.md 等算法.在单元测试时,使用到了 Powermock,记录如下. 先看下加密算法: import org.apache.comm ...

  2. 简单介绍如何使用PowerMock和Mockito来mock 1. 构造函数 2. 静态函数 3. 枚举实现的单例 4. 选择参数值做为函数的返回值(转)

    本文将简单介绍如何使用PowerMock和Mockito来mock1. 构造函数2. 静态函数3. 枚举实现的单例4. 选择参数值做为函数的返回值5. 在调用mock出来的方法中,改变方法参数的值 一 ...

  3. Mock之easymock, powermock, and mockito

    easymock, powermock, and mockito Easymock Class Mocking Limitations To be coherent with interface mo ...

  4. java.lang.AbstractMethodError: org.powermock.api.mockito.internal.mockmaker.PowerMockMaker.isTypeMockable

    [转]https://stackoverflow.com/questions/53539930/java-lang-abstractmethoderror-org-powermock-api-mock ...

  5. powerMock和mockito使用

    powerMock和mockito powermock和mockito都是做mock的框架,powermock在mockito的基础上扩展而来,支持mockito的操作(也支持别的mock框架比如ea ...

  6. 有了它(powermock)再也不担心单元测试不达标了

    为什么要写单元测试 优点:单元测试可以减少bug率,提升代码的质量.还可以通过单元测试来熟悉业务. 公司硬性要求:有些公司可能还会强制要求,每次新增代码.或者变更代码单测覆盖率要达到多少比例才能申请代 ...

  7. Mockito文档-单元测试技术

    Overview  Package   Class  Use  Tree  Deprecated  Index  Help     PREV CLASS   NEXT CLASS FRAMES     ...

  8. 使用MRUnit,Mockito和PowerMock进行Hadoop MapReduce作业的单元测试

    0.preliminary 环境搭建 Setup development environment Download the latest version of MRUnit jar from Apac ...

  9. dubbo应用程序的单元测试环境搭建(springtest,powermock,mockito)

    转:http://blog.csdn.net/yys79/article/details/66472797 最近,项目中频繁用到dubbo,而且java工程用引用了几十个关联系统的服务(如用户认证,基 ...

随机推荐

  1. PAT1032

    为了用事实说明挖掘机技术到底哪家强,PAT组织了一场挖掘机技能大赛.现请你根据比赛结果统计出技术最强的那个学校. 输入格式: 输入在第1行给出不超过105的正整数N,即参赛人数.随后N行,每行给出一位 ...

  2. quagga源码学习--BGP协议创建对等体

    现有的路由协议都是通过分布式协议逐个配置协商运行的,协议协议,一个就不需要协议咯,至少2个才能够协议着做事情嘛,不过呢,这样就出现网元过多配置困难的问题,对网管软件要求也越来越高, SDN或许可能改变 ...

  3. import from 相对路径

    项目目录 - server - static - src - - stroe - - router - - main.js - app.js src为前端文件,src目录下有main.js代码如下 i ...

  4. NOIP2017赛前模拟10月30日总结

    题目1: n个人参赛(n<=100000),每个人有一个权值··已知两个人权值绝对值之差小于等于K时,两个人都有可能赢,若大于则权值大的人赢···比赛为淘汰制,进行n-1轮·问最后可能赢的人有多 ...

  5. 上传图片获取base64位编码

    function loadImg(){ //获取文件 ].files[]; //创建读取文件的对象 var reader = new FileReader(); //创建文件读取相关的变量 var i ...

  6. callee返回正被执行的Function对象

    arguments.length是实参长度, arguments.callee.length是形参长度. function fn(a, b, c, d) { console.log(arguments ...

  7. Js 中 == 与 === 的区别

    1.对于string,number等基础类型,==和===是有区别的 1)不同类型间比较,==之比较“转化成同一类型后的值”看“值”是否相等,===如果类型不同,其结果就是不等 2)同类型比较,直接进 ...

  8. 30+ Excellent Windows Phone 7 Development Tutorials

    原文发布时间为:2012-01-16 -- 来源于本人的百度文章 [由搬家工具导入] Here are 30+ cool Windows Phone Development articles for ...

  9. java中的BitSet

    (1)BitSet类     大小可动态改变, 取值为true或false的位集合.用于表示一组布尔标志. 此类实现了一个按需增长的位向量.位 set 的每个组件都有一个 boolean 值.用非负的 ...

  10. linux内核情景分析之信号实现

    信号在进程间通信是异步的,每个进程的task_struct结构有一个sig指针,指向一个signal_struct结构 定义如下 struct signal_struct { atomic_t cou ...