zoukankan      html  css  js  c++  java
  • 使用Powermock和mockito来进行单元测试

    转载: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);

  • 相关阅读:
    Linux查看系统版本信息和设置远程终端登录
    Linux环境下安装JDK
    Windows10修改Tomcat服务端口和一台机器部署多个Tomcat
    [转]C#反射-Assembly.Load、LoadFrom与LoadFile进阶
    【转】C# lock的使用
    一个简单的C++程序及说明
    插入排序
    堆排序
    选择排序
    快速排序
  • 原文地址:https://www.cnblogs.com/ceshi2016/p/7880702.html
Copyright © 2011-2022 走看看