Mock testing means unit testing with mock objects as substitutes for real objects. By real objects I mean the objects the tested unit (class) will be using in the real application. If you have a class Calculator, that needs a dao (Data Access Object) object to load the data it needs from a database, then the dao object is a "real object". In order to test the Calculator class you will have to provide it with a dao object that has a valid connection to the database. In addition you have to insert the data needed for the test into the database.

Setting up the connection and inserting the data in the database can be a lot of work. Instead you can provide the Calculator instance with a fake dao class which just returns the data you need for the test. The fake dao class will not actually read the data from the database. The fake dao class is a mock object. A replacement for a real object which makes it easier to test the Calculator class. Purist mock testers would call such a fake dao for a stub. I will get to that distinction later.

mock测试意味着使用真实对象的替代品mock对象来进行单元测试。真实对象就是指的会在真实项目中使用的测试对象。如果你有一个calculator类,需要一个dao对象来从数据库加载数据。那么这个时候dao对象就是一个“真实的对象”。为了测试calculator类,你需要提供一个和数据库有有效联系的dao对象。而且你必须向数据库插入需要测试用的数据。

配置同数据库间的联系和插入数据可能需要大量的工作。那么你就可以为calculator实例提供一个假的dao类用来仅仅返回你需要的数据就可以了。这个假的dao类并不会真的去读取数据库的数据。这个假的dao类就是一个nock对象。用来替换真实的对象使得更容易测试calculator类。

个人理解: mock就是对象的复制品。stub可以负责指定需要测试的数据。

The situation of the Calculator using a dao object can be generalized to "a unit that uses a collaborator". The unit is the Calculator and the collaborator is the dao object. I'll express it like this:

   unit --> collaborator

The arrow means "uses". When the collaborator is exchanged with a mock (or stub), it would be expressed like this:

   unit --> mock

In a unit test situation it will look like this:

   unit test --> unit --> collaborator

... or...

   unit test --> unit --> mock

Stubs, Mocks, and Proxies

There are three types of fake objects you can use for testing: Stubs, Mocks and Proxies. Remember, a stub, mock, or proxy replaces a collaborator of the tested unit during unit test. The stubs and mocks follows Martin Fowlers definition of stubs and mocks.

A Stub is an object that implements an interface of a component, but instead of returning what the component would return when called, the stub can be configured to return a value that suits the test. Using stubs a unit test can test if a unit can handle various return values from its collaborator. Using a stub instead of a real collaborator in a unit test could be expressed like this:

stub是用来执行组件接口的对象,但是并不是调用组件应当返回的值,stub可以为了单元测试而被配置返回值。使用stubs单元测试就可以处理多种不同的返回值情况用来测试了。

  1. unit test --> stub
  2. unit test --> unit --> stub
  3. unit test asserts on results and state of unit

First the unit test creates the stub and configures its return values. Then the unit test creates the unit and sets the stub on it. Now the unit test calls the unit which in turn calls the stub. Finally the unit test makes assertions about the results of the method calls on the unit.

A Mock is like a stub, only it also has methods that make it possible determine what methods where called on the Mock. Using a mock it is thus possible to both test if the unit can handle various return values correctly, and also if the unit uses the collaborator correctly. For instance, you cannot see by the value returned from a dao object whether the data was read from the database using a Statement or a PreparedStatement. Nor can you see if the connection.close() method was called before returning the value. This is possible with mocks. In other words, mocks makes it possible to test a units complete interaction with a collaborator. Not just the collaborator methods that return values used by the unit. Using a mock in a unit test could be expressed like this:

首先单元测试创建stub并且配置他的返回值。然后单元测试创建单元并且设置stub。现在单元测试调用单元就是调用stub。最后单元测试使用assertions来处理单元的方法调用结果。

mock类似stub,只是他也有方法可以来确定mock调用什么方法和什么时候调用。如果unit可以正确处理多个返回值,那么使用mock就可以都测试到这些结果。可以用来测试交互行为。

  1. unit test --> mock
  2. unit test --> unit --> mock
  3. unit test asserts on result and state of unit
  4. unit test asserts on the methods called on mock

First the unit test creates the mock and configures its return values. Then the unit test creates the unit and sets the mock on it. Now the unit test calls the unit which in turn calls the mock. Finally the unit test makes assertions about the results of the method calls on the unit. The unit test also makes assertions on what mehods were called on the mock.

首先unit test创建mock并且配置他的返回值。然后单元测试创建单元并且为他们设置mock。现在单元测试调用unit,unit调用mock。最后使用assertion来判断结果。

Proxies in mock testing are mock objects that delegate the method calls to real collaborator objects, but still records internally what methods were called on the proxy. Thus proxies makes it possible to do mock testing with real collaborators. Using a proxy in a unit test could be expressed like this:

  1. unit test --> collaborator
  2. unit test --> proxy
  3. unit test --> unit --> proxy --> collaborator
  4. unit test asserts on result and state of unit
  5. unit test asserts on methods called on proxy

First the unit test creates the collaborator. Then the unit test creates the proxy for the collaborator. Third the unit test creates the unit and sets the proxy on it. Now the unit test calls the unit which in turn calls the proxy. Finally the unit test makes assertions about the results of the method calls on the unit. The unit test also makes assertions on what mehods were called on the proxy.

Stub, Mock, and Proxy Testing with Testimonial

There are several popular mock testing API's available. Among others JMock and EasyMock. As of writing these two API's do not support proxies as described above. Note: They *use* java.lang.reflect.Proxy instances to implement their dynamic mocks. But that is not the same as the test proxies described above. These API's can only be used with stubs and mocks, not proxy for real collaborators. I'm sure they'll add it in the future.

For the code examples in this text I will be using my own testing API, Butterfly Testing Tools . I developed Butterfly Testing Tools because I needed the proxy testing feature that neither JMock nor EasyMock has. In addition the API's could be designed a bit more straigtforward and flexible, in my opinion (that, I guess, is a matter of personal style).

First lets see how to create stub for an interface:

   Connection connection =
(Connection) MockFactory.createProxy(Connection.class);

The connection variable is a stub of the Connection interface. I can now call methods on the connection instance just as if it was a real database connection. The methods will only return null though, since the stub is not configured to return any special value. In order to configure the stub to return the values appropriate for your test, you must obtain the Mock for the stub. Here is how that is done:

   IMock mock = MockFactory.getMock(connection);

Now you have the mock associated with the stub. One of the methods the IMock interface has is

   addReturnValue(Object returnValue);

Using the addReturnValue method you can add return values to the stub. You can add as many as you want. The return values will be returned from the stub in the same sequence they were added. Once a return value has been returned it is removed from the stub, just like in a queue. Note: The sequence of added return values must match the sequence of called methods on the stub! If you add a String "myReturnValue" as return value to the stub and then call connection.prepareStatement("select * from houses") which returns a PreparedStatement, you will get an exception. The String return value cannot be returned from the connection.prepareStatement("..."); You will have to make sure yourself that the return values and called methods on the stub match.

Once you have the IMock instance for a stub you can also make assumptions about what methods were called on the stub. The IMock interface has a series of assertCalled(MethodInvocation) methods for this purpose. Here is an example:

   mock.assertCalled(new MethodInvocation("close"));

If the connection.close() method has not been called a java.lang.AssertionError is thrown. If you are using JUnit for your unit test JUnit will catch the AssertionError and report that the test failed. There are other assertCalled() etc. methods on the IMock interface. See the Testimonial project for more info.

The last thing I will show is how to make a proxy for a real connection and get the mock associated with the proxy:

   //opens a real database connection.
Connection realConnection = getConnection();
Connection proxyConnection = MockFactory.createProxy(realConnection);
IMock mock = MockFactory.getMock(proxyConnection);

Simple, isn't it? It's just the same as when creating stubs for interfaces. You just provide the real collaborator to the MockFactory instead of an interface (class object). The proxyConnection will record all methods called on it before forwarding the method call to the realConnection instance. That way you can use the proxyConnection just as fine as a real connection, and at the same time make mock assertions about what methods were called on it.

You can even turn the proxyConnection into a stub temporarily by adding a return value to the proxy via the mock.addReturnValue(...). When the proxyConnection sees a return value it will return that instead of forwarding the call to the realConnection. Once all return values have been returned the proxyConnection will continue forwarding the method calls to the realConnection instance. That way you can switch between using the proxyConnection as a real connection or a stub. Smart, isn't?

It is not just database connections that are useful to mock. Any collaborator of a tested unit can potentially be mocked during testing. Whether to test a unit using a mock or a real collaborator depends on the situation. Proxies make it possible to do both at the same time, and even stub some method calls along the way. For more information about mock testing with Testimonial, see the Testimonial project page.

Stub, Mock and Proxy Testing的更多相关文章

  1. TDD学习笔记【六】一Unit Test - Stub, Mock, Fake 简介

    这篇文章简介一下,如何通过 mock framework,来辅助我们更便利地模拟目标对象的依赖对象,而不必手工敲堆只为了这次测试而存在的辅助类型. 而模拟目标对象的部分,常见的有 stub objec ...

  2. Rails 5 Test Prescriptions 第7章 double stub mock

    https://relishapp.com/rspec/rspec-mocks/v/3-7/docs/basics/test-doubles 你有一个问题,如果想为程序添加一个信用卡程序用于自己挣钱. ...

  3. Stub和Mock的理解

    我对Stub和Mock的理解 介绍 使用测试驱动开发大半年了,我还是对Stub和Mock的认识比较模糊,没有进行系统整理. 今天查阅了相关资料,觉得写得很不错,所以我试图在博文中对资料进行整理一下,再 ...

  4. [转]软件测试- 3 - Mock 和Stub的区别

    由于一直没有完全搞明白Mock和Stub的区别,所以查了很多文章,而这一篇是做好的: http://yuan.iteye.com/blog/470418 尤其是8楼,Frostred的发言,描述地相当 ...

  5. android中Stub Proxy答疑

    在上篇添加账户源码解析的博文中,我们发现功能是由AccountManager的mService成员来实现.而mService其实是AccountManagerService,如果对android系统有 ...

  6. [转载] google mock cookbook

    原文: https://code.google.com/p/googlemock/wiki/CookBook Creating Mock Classes Mocking Private or Prot ...

  7. spring security+cas(cas proxy配置)

    什么时候会用到代理proxy模式? 举一个例子:有两个应用App1和App2,它们都是受Cas服务器保护的,即请求它们时都需要通过Cas 服务器的认证.现在需要在App1中通过Http请求访问App2 ...

  8. Dubbo学习笔记-RPC扩展和本地Mock

    1.Dubbo介绍 Dubbo,一个是一款高性能Java RPC框架.私以为有中文官方文档,就不再重复介绍了 2.RPC扩展-本地存根stub RPC扩展功能:提前效验参数,缓存调用结果,日志记录等等 ...

  9. Mockito鸡尾酒第一杯 单测Mock

    鸡尾酒 Mockito是Java的单元测试Mock框架. 它的logo是一杯古巴最著名的鸡尾酒Mojito, Mojito鸡尾酒,源自古巴的哈瓦那,带有浓厚的加勒比海风情. 并不浓烈,但是喝一杯下去, ...

随机推荐

  1. BETA0

    目录 过去存在的问题 任务分工 规范 后端总结 卉卉 家灿 前端总结 绪佩 青元 恺琳 宇恒 丹丹 算法&API接口 家伟 鸿杰 一好 文档&博客撰写 政演 产品功能 我们已经坐了哪些 ...

  2. android 自定义控件之下拉刷新源码详解

    下拉刷新 是请求网络数据中经常会用的一种功能. 实现步骤如下: 1.新建项目   PullToRefreshDemo,定义下拉显示的头部布局pull_to_refresh_refresh.xml &l ...

  3. SqlHelper——数据库小助手

    SqlHelper其实就是一个类. 早就听说过"SqlHelper"这个名词,也查过相关的资料,但还是一头雾水.当真的去实践去用它时,就会发现其实它没那么神秘. 当敲第一个窗体的时 ...

  4. [POI2015][bzoj4383] Pustynia [线段树优化建图+拓扑排序]

    题面 bzoj权限题传送门 luogu传送门 思路 首先,这个题目显然可以从所有小的点往大的连边,然后如果没环就一定可行,从起点(入读为0)开始构造就好了 但是问题来了,如果每个都连的话,本题中边数是 ...

  5. 【BZOJ 2458 最小三角形】

    Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1551  Solved: 549[Submit][Status][Discuss] Descripti ...

  6. 百度之星初赛(A)——T1

    小C的倍数问题 Problem Description 根据小学数学的知识,我们知道一个正整数x是3的倍数的条件是x每一位加起来的和是3的倍数.反之,如果一个数每一位加起来是3的倍数,则这个数肯定是3 ...

  7. PHP高性能开发-多进程开发

    硬件多核时代的软件业以前计算能力的提升一直在摩尔定律的指引下,沿着提升CPU时钟频率这条道路前进,从初期的几十MHz到如今的几GHz.但是,进入2002年以 来,CPU提升主频的困难越来越大,因为主频 ...

  8. 发布windows服务的批处理

    安装bat: C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe WatchWinService.exe pause 卸载bat ...

  9. symfony3常用记忆

    1.控制器里获取当前用户信息 $user = $this->getUser(); 2.判断当前用户是否登录 // yay! Use this to see if the user is logg ...

  10. C# WeakReference(弱引用)

    WeakReference(弱引用)我们平常用的都是对象的强引用,如果有强引用存在,GC是不会回收对象的.我们能不能同时保持对对象的引用,而又可以让GC需要的时候回收这个对象呢?.NET中提供了Wea ...