您的位置:首页 > 其它

使用Mockito进行单元测试

2017-06-20 14:37 375 查看
http://qiuguo0205.iteye.com/blog/1443344


1. 为什么使用Mockito来进行单元测试?

 

回答这个问题需要回答两个方面,第一个是为什么使用mock?mock其实是一种工具的简称,他最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。

 

比如一段代码有这样的依赖:

 



 

当我们需要测试A类的时候,如果没有mock,则我们需要把整个依赖树都构建出来,而使用mock的话就可以将结构分解开,像下面这样:



 

还有一个问题是mock工具那么多,为什么我们要用mockito呢?原因很简单:他非常好用!

他使用执行后验证的模型,语法更简洁并且更加贴近程序员的思考方式,能够模拟类而不仅仅是接口等等。总之如果你想使用mock的话,试用mockito,你不会后悔的:)

 

引用的图摘自http://www.theserverside.com/news/1365050/Using-JMock-in-Test-Driven-Development,那里对mock的使用有很好的介绍。

http://www.sizovpoint.com/2009/03/java-mock-frameworks-comparison.html是一篇非常好的mock工具比较的文章,我就是从它认识的mockito,他也有对mock使用的精彩介绍。

还有一篇文章总结了mockito的好处:http://java.dzone.com/articles/mockito-pros-cons-and-best

 

当然,要想真正了解mockito的好处,就必须写写代码练习一下了。

 


2. Mockito使用实例

这里的代码基本都是从http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html

摘出来的,然后加上了自己的一些学习验证,这个网页挺重要的,会多次提到,以后就简称”网页“了。让我们通过这些实例来看看mockito的强大功能吧:

 


1. 让我们验证一些行为吧

 

 

Java代码  


//Let's import Mockito statically so that the code looks clearer  

import static org.mockito.Mockito.*;  

  

//mock creation  

List mockedList = mock(List.class);  

  

// using mock object  

mockedList.add("one");  

mockedList.clear();  

mockedList.add("3"); // no verify? OK  

  

// verification  

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

verify(mockedList).clear();  

// verify(mockedList).add("2"); // this will throw an exception  

 

首先通过这段代码介绍什么是mock:首先使用Mockito的静态方法mock,我们就可以创建一个类的mock实例,这个mock实例拥有这个List的所有方法接口,并且给这些方法以最基本的实现:如果是返回void,他什么都不做,否则他就返回null或0等基本类型的值。比如中间的三句调用了mock的方法,即使将来不验证也没有任何关系。

 

在验证阶段,当我们验证这个mock的方法add("one")是否被调用的时候,他不会抛出异常,因为我们确实调用了这个方法,但是当我们验证它是否调用add("2")的时候,就会抛出异常,说明我们没有调用过这个方法,此时的测试就会失败。

 

所以验证的意思是”查看我们到底有没有调用过mock的这个方法“。

 


2. 它能提供桩[stub]测试吗?

 

相信这样的场景我们都遇到过,有一个方法的输入是一个List,在这个方法中我们遍历这个List,读取数据,做相应的操作。往常我们可能需要自己创建一个ArrayList,并且将需要的测试的参数add进list中,这样就可以分别进行测试了。下面看看使用mockito是怎么做到的:

 

 

Java代码  


// You can mock concrete classes, not only interfaces  

LinkedList mockedList = mock(LinkedList.class);  

  

// stubbing  

when(mockedList.get(0)).thenReturn("first");  

when(mockedList.get(1)).thenThrow(new RuntimeException());  

  

// following prints "first"  

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

// following throws runtime exception  

System.out.println(mockedList.get(1));  

// following prints "null" because get(999) was not stubbed  

System.out.println(mockedList.get(999));  

  

// Although it is possible to verify a stubbed invocation, usually it's just redundant  

// See http://monkeyisland.pl/2008/04/26/asking-and-telling  

verify(mockedList, atLeast(2)).get(0);  

 

首先我们可以看到mockito是可以mock类而不仅仅是接口的,而stub的语法也非常接近人的阅读习惯:when(mockedList.get(0)).thenReturn("first"); 当调用get(0)的时候返回"first"。

 

这里需要注意以下几点:

【1】mock实例默认的会给所有的方法添加基本实现:返回null或空集合,或者0等基本类型的值。

【2】当我们连续两次为同一个方法使用stub的时候,他只会只用最新的一次。

【3】一旦这个方法被stub了,就会一直返回这个stub的值。

像下面这段代码,你猜会打印什么?

 

 

Java代码  


when(mockedList.get(0)).thenReturn("first");  

when(mockedList.get(0)).thenReturn("oops");  

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

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


  3. 参数匹配

下面我们看看mockito强大的参数匹配机制,当mockito执行verify的时候,它实际上对参数执行的是自然地java方式——equals方法。有事我们需要对参数进行灵活匹配的时候就可以用到”参数匹配器“【argument matchers】了

 

 

Java代码  


// stubbing using built-in anyInt() argument matcher  

when(mockedList.get(anyInt())).thenReturn("element");  

  

// following prints "element"  

System.out.println(mockedList.get(999));  

  

// you can also verify using an argument matcher  

verify(mockedList).get(anyInt());  

 

这里的anyInt是mockito内建的众多方法之一,其他可以参考mockito主页上的信息,你也可以调用hamcrest的matchers。

 

警告:若方法中的某一个参数使用了matcher,则所有的参数都必须使用matcher:

 

 

Java代码  


// correct  

verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));  

// will throw exception  

verify(mock).someMethod(anyInt(), anyString(), "third argument");  


  4. 继续讨论Verification

 

前面的例子都是和网页上的例子一一对应的,现在我们集中讨论一下mockito在verify上提供的强大功能,大部分例子都很简单,所以我基本就是简单的罗列:

 

# 验证方法被调用的次数 网页例子4

 

Java代码  


//using mock   

mockedList.add("once");  

  

mockedList.add("twice");  

mockedList.add("twice");  

  

mockedList.add("three times");  

mockedList.add("three times");  

mockedList.add("three times");  

  

//following two verifications work exactly the same - times(1) is used by default  

verify(mockedList).add("once");  

verify(mockedList, times(1)).add("once");  

  

//exact number of invocations verification  

verify(mockedList, times(2)).add("twice");  

verify(mockedList, times(3)).add("three times");  

  

//verification using never(). never() is an alias to times(0)  

verify(mockedList, never()).add("never happened");  

  

//verification using atLeast()/atMost()  

verify(mockedList, atLeastOnce()).add("three times");  

verify(mockedList, atLeast(2)).add("five times");  

verify(mockedList, atMost(5)).add("three times");  

 

# 按顺序验证  网页例子6

 

 

Java代码  


// A. Single mock whose methods must be invoked in a particular order  

List singleMock = mock(List.class);  

  

//using a single mock  

singleMock.add("was added first");  

singleMock.add("was added second");  

  

//create an inOrder verifier for a single mock  

InOrder inOrder = inOrder(singleMock);  

  

//following will make sure that add is first called with "was added first, then with "was added second"  

inOrder.verify(singleMock).add("was added first");  

inOrder.verify(singleMock).add("was added second");  

  

// B. Multiple mocks that must be used in a particular order  

List firstMock = mock(List.class);  

List secondMock = mock(List.class);  

  

//using mocks  

firstMock.add("was called first");  

secondMock.add("was called second");  

  

//create inOrder object passing any mocks that need to be verified in order  

InOrder inOrder = inOrder(firstMock, secondMock);  

  

//following will make sure that firstMock was called before secondMock  

inOrder.verify(firstMock).add("was called first");  

inOrder.verify(secondMock).add("was called second");  

  

// Oh, and A + B can be mixed together at will  

 

# 确保某些方法没有被调用  网页例子7

 

 

Java代码  


//using mocks - only mockOne is interacted  

mockOne.add("one");  

  

//ordinary verification  

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

  

//verify that method was never called on a mock  

verify(mockOne, never()).add("two");  

  

//verify that other mocks were not interacted  

verifyZeroInteractions(mockTwo, mockThree);  

 

# 从前面的例子我们可以看到,能够很容易地找到冗余的调用  网页例子8

 

 

Java代码  


//using mocks  

mockedList.add("one");  

mockedList.add("two");  

  

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

  

//following verification will fail   

verifyNoMoreInteractions(mockedList);  

 

 

OK,看过Mockito的 mock 和 verify的能力,你可能已经喜欢上Mockito了,不过这只是Mockito强大功能的一部分,下一篇接着翻译我个人用的最多的stub的功能,真的不可错过,看完之后你绝对能够惊叹Mockito的实力的;-)

一篇中介绍了Mockito的基本信息,现在接着介绍Mockito强大的stub功能

 

2. Mockito使用实例


5. 对连续的调用进行不同的返回 (iterator-style stubbing)

还记得在实例2中说道当我们连续两次为同一个方法使用stub的时候,他只会使用最新的一次。但是在某一个方法中我们确实有很多的调用怎么办呢?mockito当然想到这一点了:

 

 

Java代码  


when(mock.someMethod("some arg"))  

  .thenThrow(new RuntimeException())  

  .thenReturn("foo");  

  

//First call: throws runtime exception:  

mock.someMethod("some arg");  

  

//Second call: prints "foo"  

System.out.println(mock.someMethod("some arg"));  

  

//Any consecutive call: prints "foo" as well (last stubbing wins).   

System.out.println(mock.someMethod("some arg"));  

 

 

当然我们也可以将第一句写的更简单一些:

 

Java代码  


when(mock.someMethod("some arg"))  

  .thenReturn("one", "two", "three");  

参见网页例子10。

 

6. 使用回调进行stub【Stubbing with callbacks】

 

我们可以使用generic的Answer接口来让mock对象执行我们期望它执行的内容。比如我们可以查看调用方法的参数信息,并根据这个信息进行不同的处理,这可以使我们的stub变得十分的灵活。

 

 

Java代码  


when(mock.someMethod(anyString())).thenAnswer(new Answer() {  

    Object answer(InvocationOnMock invocation) {  

        Object[] args = invocation.getArguments();  

        Object mock = invocation.getMock();  

        return "called with arguments: " + args;  

    }  

});  

  

//Following prints "called with arguments: foo"  

System.out.println(mock.someMethod("foo"));  

参见网页例子11。

7. 使用 doThrow()|doAnswer()|doNothing()|doReturn() 来 stub void方法

 

void方法也需要stub?呵呵,实际生活中我们应该只会用到doThrow来模拟这个void方法出错的情况吧,anyway,mockito提供了四个方法,发挥你的想象力吧:-)

Java代码  


doThrow(new RuntimeException()).when(mockedList).clear();  

  

//following throws RuntimeException:  

mockedList.clear();  

  参见网页例子12。

 

8. 在真实的对象上进行spy

 

 

spy的意思是你可以修改某个真实对象的某些方法的行为特征,而不改变他的基本行为特征,这种策略的使用跟AOP有点类似。下面举一个例子来说明:

 

Java代码  


List list = new LinkedList();  

List spy = spy(list);  

  

//optionally, you can stub out some methods:  

when(spy.size()).thenReturn(100);  

   

//using the spy calls <b>real</b> 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");  

 

可以看到spy保留了list的大部分功能,只是将它的size方法改写了。不过spy在使用的时候有很多地方需要注意,一不小心就会导致问题,所以不到万不得已还是不要用spy。下面介绍两个spy的陷阱:

 

【1】有时我们无法使用when的方式来spy,此时我们就需要使用doReturn|Answer|Throw() 等方式来进行spy了:

 

Java代码  


List list = new LinkedList();  

List spy = spy(list);  

  

//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的时候实际已经调用了get(0)方法,这个时候将直接抛出异常,所以此时应该使用doReturn来进行spy

 

【2】spy实际上是对对象做了一个拷贝,就像上面的,如果我们直接看list这个对象,它实际上只执行了这样一句话List list = new LinkedList();

【3】 无法spy final方法。

参见网页例子13。

 

9. 其他高级特性

 

当说到高级特性,我们就是说那些基本用不到,但是当我们想用的时候就非常顺手的功能,比如:

# 改变没有stub方法的默认返回值  网页例子14

# 抓取参数进行assert验证  网页例子15

# 重置mock对象   网页例子16

由于浙西而使用机会比较小,这里就不详述了,更多的例子还是参考网页http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html

 

 


3.小结

不知看了以上的内容,你是否觉得Mockito是使用mock进行单元测试的理想工具,如果是的话,就猛击

http://code.google.com/p/mockito/的主页下载并使用吧:-)

------  http://blog.csdn.net/fireofjava/article/details/42025353

PowerMockito.doReturn().when()与Mockito.when().thenReturn()的区别

1.当使用PowerMockito.doReturn(null).when(handler, "getFareRules", Integer.valueOf(requestDTO.getFareId()), "GB");时
handler的getFareRules方法不会被真的调用,在getFareRules里面打一些日志,这些日志不会输出,也就是说根本没有真的去调用该方法,而是直接
调用了代理方法,返回在doReturn设置的值。

2.当使用Mockito.when(handler.getFareRules(Integer.valueOf(requestDTO.getFareId()), "GB")).thenReturn(new FareRules());时
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: