使用配置文件定义 Mock 对象,创建高效、灵活的测试用例

发表于:2007-10-30 12:13

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:郑闽睿 黄湘平    来源:网络转载

        本文主要讨论如何利用配置文件对 Mock 对象以及它的行为进行描述,从而分离测试数据和代码,创建高效、灵活的测试用例。同时,本文给出了一套基于开源项目 EasyMock 的实现,并通过一个示例来说明如何利用这一实现编写测试用例。

        使用 Mock 方法能够模拟协同模块或领域对象,从而把测试与测试边界以外的对象隔离开。使单元测试顺利进行。然而,Mock 方法在辅助测试的同时,也给开发或测试人员带来额外的编码工作。另外,由于 Mock 对象本身并不能对测试数据进行管理,因此测试数据的变动和 Mock 对象本身的变动,可能就会极大的增加编译和部署的时间。

        本文提出一种利用 XML 文件对 Mock 对象进行配置的机制,并在开源项目 EasyMock 的基础上实现了这种机制。实际上,读者可以基于任何的自己熟悉的 xMock 项目来实现这里的思想。

1.Mock对象的创建方法

        开发和测试人员在利用 Mock 方法进行单元测试时发现,编写自定义 Mock 对象会带来大量额外的编码工作:如果为测试中用到的每一个协同模块或领域对象手动编写 Mock 对象,最终的结果将是 Mock 对象的数目随着系统中实际对象数目的增长而增长。此外,这些为创建 Mock 对象而编写的代码也很有可能引入错误。

        目前,由许多开源项目对动态构建 Mock 对象提供了支持,这些项目能够根据现有的接口或类动态生成 Mock 对象,从而避免了编写自定义的 Mock 对象,这样不仅能减少一定的编码工作,也可以降低错误引入的可能。

        EasyMock 就是这些开源框架中的一个,它是一套通过简单的方法对于给定的接口生成 Mock 对象的类库。它提供对接口的模拟,能够通过录制、回放、检查三个步骤来完成大体的测试过程。EasyMock 可以验证方法的调用种类、次数和顺序,可以令 Mock 对象返回指定的值或抛出指定异常。通过 EasyMock,开发或测试人员能够比较方便的创建 Mock 对象,在一定程度上减少了创建 Mock 对象所带来的工作量。

2.EasyMock 使用示例

        EasyMock 的使用方法和原理的详细说明请参见 "EasyMock 使用方法和原理剖析" 一文。在这里,我们仅以 HttpServletRequest 为例对 EasyMock 的功能做简单说明。

        在部署到 Servlet 容器之前,需要和 HttpServletRequest 进行交互的模块可以通过构建 Mock 对象的方式进行单元测试。下面是使用 EasyMock(version 2.3)构建 Mock 对象进行简单测试的例子:


清单1:EasyMock 示例
public class HttpServletRequestUtil {
  public static boolean validate(HttpServletRequest request) {
    String host = request.getHeader("Host");
    return host.startsWith("www.ibm.com");
  }
}
public class HttpServletRequestTestCase extends TestCase {
  public void testHttpSevletRequest() {
    HttpServletRequest mockRequest = createMock(HttpServletRequest.class);
    mockRequest.getHeader("Host");
    expectLastCall().andReturn("www.ibm.com:80").times(1);
   
    replay(mockRequest);
assertTrue(HttpServletRequestUtil.validate(mockRequest));
verify(mockRequest);
  }
}

        首先,我们通过 EasyMock 提供的静态方法 createMock 创建 Mock 对象 mockRequest。当 Mock 对象创建好以后,我们就可以对 Mock 对象的预期行为和输出进行设定。对预期行为和输出的设定分成两个部分:(1)对指定方法进行调用;(2)对预期输出进行设定。在上例中,mockRequest.getHeader("Host"); 对 Mock 对象的 getHeader 方法进行了调用,之后用 expectLastCall().andReturn("www.ibm.com:80").times(1) 对Mock对象的预期输出进行了设定。andReturn 方法设定了当 getHeader 方法被调用时,将返回字符串 "www.ibm.com:80",times 方法设定了该方法预期被调用的次数是1。

        在结束对 Mock 对象预期行为和方法的设定之后,我们可以调用 replay 静态方法将 mockRequest 对象切换成回放状态。在回放状态下,Mock 对象的方法调用将返回预先设定的输出。在上例中,HttpServletRequestUtil 类的 validate 方法对 mockRequest 的 getHeader 方法进行了调用,并对得到的值进行验证。

        最后,我们可以用 verify 方法来验证预期方法的调用是否真的完成了。如果将上例中 expectLastCall().andReturn("www.ibm.com:80").times(1) 设定的调用次数修改为2,而实际测试中只调用了一次该方法,您将会看到以下的错误:


清单2:verify 验证错误
java.lang.AssertionError:
  Expectation failure on verify:
    getHeader("Host"): expected: 2, actual: 1
at org.easymock.internal.MocksControl.verify
at org.easymock.EasyMock.verify
at org.easymock.demo.testcase.HttpServletRequestTestCase.testHttpSevletRequest

        通过示例,我们了解了 EasyMock 的使用方法。EasyMock 能为单元测试提供了一定的便利,然而,它也有一些明显的不足之处:

  • 测试数据和预期结果以编码的形式写在测试用例中,测试数据的任何微小变化都会导致代码的重新编译和部署;
  • 被测试模块所包含的方法和参数硬编码在测试代码中,方法或参数的变化将导致所有相关测试代码的修改(例如 HttpServletRequest 中的参数常常会在开发过程中发生改变,这会影响大量测试代码);
  • 单元测试的测试过程包含在测试代码中,当测试用例发生变化,测试代码有可能需要全部重写,造成代码的频繁修改和引入错误的机会。

3.利用 XML 文件配置 Mock 对象

        为了改进目前 EasyMock 使用方法中存在的不足,我们需要引入配置文件来对 Mock 对象进行定义。我们的目标是通过配置文件的使用来实现测试代码和数据的分离。当开发人员因为测试用例的变化而需要改变 Mock 对象的测试行为时,就可以直接对配置文件作出改动,而无需修改测试代码。

        构建 Mock 对象需要以下两方面的信息:(1)Mock 对象对应的接口或类信息;(2)Mock 对象的预期行为与输出。如果将以上两类信息配置在文件中,通过对配置文件的解析来构造 Mock 对象,就可以实现测试代码和数据分离的目标,从而改进现有 Mock 对象构造方法中的不足。

        本文在提出使用配置文件定义 Mock 对象这一机制的同时,也提供了一个基于 EasyMock 的实现。我们将这一实现称为 XMLEasyMock。XMLEasyMock 的完整实现和相关的测试代码都可以在 xmleasymock.zip 中找到。如果您使用 Eclipse 作为 IDE,那么您可以将它导入您的 Workspace(如下图):


图1:导入 xmleasymock.zip 后的 workspace
导入 xmleasymock.zip 后的 workspace

        在 XMLEasyMock 中,我们选用 XML 文件作为 Mock 对象的配置文件,XML 文件的自定义和结构特性使得它成为描述 Mock 对象最佳的选择。根据以上对 Mock 对象信息配置的分析,我们可以给出 Mock 对象配置文件的模板:

41/41234>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号