首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 开源软件 >

EasyMock 使用方法与原理辨析

2012-06-26 
EasyMock 使用方法与原理剖析https://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/?Mock 方

EasyMock 使用方法与原理剖析

https://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/

?

Mock 方法是单元测试中常见的一种技术,它的主要作用是模拟一些在应用中不容易构造或者比较复杂的对象,从而把测试与测试边界以外的对象隔离开。

编写自定义的 Mock 对象需要额外的编码工作,同时也可能引入错误。EasyMock 提供了根据指定接口动态构建 Mock 对象的方法,避免了手工编写 Mock 对象。本文将向您展示如何使用 EasyMock 进行单元测试,并对 EasyMock 的原理进行分析。

设定预期异常抛出

对象行为的预期输出除了可能是返回值外,还有可能是抛出异常。IExpectationSetters 提供了设定预期抛出异常的方法:

下面是示例代码中的一个接口 SalesOrder,它的实现类 SalesOrderImpl 的主要功能是从数据库中读取一个 Sales Order 的 Region 和 Total Price,并根据读取的数据计算该 Sales Order 的 Price Level(完整的实现代码都可以在 src.zip 中找到):


清单2:SalesOrder 接口

?

在这个示例中,我们首先创建了 ResultSet 的 Mock 对象 moResultSet,并记录该 Mock 对象的预期行为。之后我们调用了 control.replay(),将 Mock 对象的状态置为 Replay 状态。在实际的测试阶段,Sales Order 对象的 loadDataFromDB 方法调用了 mockResultSet 对象的 getStringgetDouble 方法读取 mockResultSet 中的数据。Sales Order 对象根据读取的数据计算出 Price Level,并和预期输出进行比较。

EasyMock 类提供的 createNiceMock() 方法创建。类似的,你也可以用 IMocksControl 实例来创建一个 Nice Mock 对象。

和开发人员联系最紧密的是 EasyMock 类,这个类提供了 createMock、replay、verify 等方法以及所有预定义的参数匹配器。

我们知道 Mock 对象有两种创建方式:一种是通过 EasyMock 类提供的 createMock 方法创建,另一种是通过 EasyMock 类的 createControl 方法得到一个 IMocksControl 实例,再由这个 IMocksControl 实例创建 Mock 对象。其实,无论通过哪种方法获得 Mock 对象,EasyMock 都会生成一个 IMocksControl 的实例,只不过第一种方式中的 IMocksControl 的实例对开发人员不可见而已。这个 IMocksControl 的实例,其实就是 MocksControl 类的一个对象。MocksControl 类提供了 andReturn、andThrow、times、createMock 等方法。

MocksControl 类中包含了两个重要的成员变量,分别是接口 IMocksBehaviorIMocksControlState 的实例。其中,IMocksBehavior 的实现类 MocksBehavior 是 EasyMock 的核心类,它保存着一个 ExpectedInvocationAndResult 对象的一个列表,而 ExpectedInvocationAndResult 对象中包含着 Mock 对象方法调用和预期结果的映射。MocksBehavior 类提供了 addExpectedaddActual 方法用于添加预期行为和实际调用。

MocksControl 类中包含的另一个成员变量是 IMocksControlState 实例。IMocksControlState 拥有两个不同的实现类:RecordStateReplayState。顾名思义,RecordState 是 Mock 对象在 Record 状态时的支持类,它提供了 invoke 方法在 Record 状态下的实现。此外,它还提供了 andReturn、andThrow、times 等方法的实现。ReplayState 是 Mock 对象在 Replay 状态下的支持类,它提供了 invoke 方法在 Replay 状态下的实现。在 ReplayState 中,andReturn、andThrow、times 等方法的实现都是抛出IllegalStateException,因为在 Replay 阶段,开发人员不应该再调用这些方法。

当我们调用 MocksControlcreateMock 方法时,该方法首先会生成一个 JavaProxyFactory 类的对象。JavaProxyFactory 是接口 IProxyFactory 的实现类,它的主要功能就是通过 java.lang.reflect.Proxy 对指定的接口创建动态代理实例,也就是开发人员在外部看到的 Mock 对象。

在创建动态代理的同时,应当提供 InvocationHandler 的实现类。MockInvocationHandler 实现了这个接口,它的 invoke 方法主要的功能是根据 Mock 对象状态的不同而分别调用 RecordStateinvoke 实现或是 ReplayStateinvoke 实现。

EasyMock 类的 createMock 方法被调用时,它首先创建一个 MocksControl 对象,并调用该对象的 createMock 方法创建一个 JavaProxyFactory 对象和一个 MockInvocationHandler 对象。JavaProxyFactory 对象将 MockInvocationHandler 对象作为参数,通过 java.lang.reflect.Proxy 类的 newProxyInstance 静态方法创建一个动态代理。

MockInvocationHandlerinvoke 方法被调用时,它首先通过 reportLastControl 静态方法将 Mock 对象对应的 MocksControl 对象报告给 LastControl 类,LastControl 类将该对象保存在一个 ThreadLocal 变量中。接着,MockInvocationHandler 将创建一个 Invocation 对象,这个对象将保存预期调用的 Mock 对象、方法和预期参数。

在记录 Mock 对象预期行为时,Mock 对象的状态是 Record 状态,因此 RecordState 对象的 invoke 方法将被调用。这个方法首先调用 LastControlpullMatchers 方法获取参数匹配器。如果您还记得自定义参数匹配器的过程,应该能想起参数匹配器被调用时会将实现类的实例报告给 EasyMock,而这个实例最终保存在 LastControl 中。如果没有指定参数匹配器,默认的匹配器将会返回给 RecordState

根据 Invocation 对象和参数匹配器,RecordState 将创建一个 ExpectedInvocation 对象并保存下来。

在对预期方法进行调用之后,我们可以对该方法的预期输出进行设定。我们以

在预期方法被调用时,Mock 对象对应的 MocksControl 对象引用已经记录在 LastControl 中,expectLastCall 方法通过调用 LastControllastControl 方法可以获得这个引用。MocksControl 对象的 andReturn 方法在 Mock 对象 Record 状态下会调用 RecordStateandReturn 方法,将设定的预期输出以 Result 对象的形式记录下来,保存在 RecordState 的 lastResult 变量中。

MocksControltimes 方法被调用时,它会检查 RecordState 的 lastResult 变量是否为空。如果不为空,则将 lastResult 和预期方法被调用时创建的 ExpectedInvocation 对象一起,作为参数传递给 MocksBehavioraddExpected 方法。MocksBehavioraddExpected 方法将这些信息保存在数据列表中。

在 Replay 状态下,MockInvocationHandler 会调用 ReplayStateinvoke 方法。该方法会把 Mock 对象通过 MocksBehavioraddActual 方法添加到实际调用列表中,该列表在 verify 方法被调用时将被用到。同时,addActual 方法会根据实际方法调用与预期方法调用进行匹配,返回对应的 Result 对象。调用 Result 对象的 answer 方法就可以获取该方法调用的输出。

描述名字大小下载方法本文用到的示例代码src.zip176KBHTTP

关于下载方法的信息

?

参考资料

学习

如果您想要获得 EasyMock 完整的文档和 API,您可以访问 EasyMock 的主页:http://www.easymock.org/。

您可以在 JUnit 的主页上找到完整的文档和相关下载:http://www.junit.org/index.htm。

通过 Developer Works 上的文章,您可以和其它的框架做比较:利用 Eclipse 进行单元测试。

获得产品和技术

在Source Forge上,你可以下载到最新的 EasyMock 相关代码:http://sourceforge.net/project/showfiles.php?group_id=82958。

Eclipse 的相关下载可以在 http://www.eclipse.org/ 上找到。

热点排行