Mock 对象 创建高效灵活的测试用例文档格式.docx
- 文档编号:5227584
- 上传时间:2023-05-04
- 格式:DOCX
- 页数:10
- 大小:21.27KB
Mock 对象 创建高效灵活的测试用例文档格式.docx
《Mock 对象 创建高效灵活的测试用例文档格式.docx》由会员分享,可在线阅读,更多相关《Mock 对象 创建高效灵活的测试用例文档格式.docx(10页珍藏版)》请在冰点文库上搜索。
通过EasyMock,开发或测试人员能够比较方便的创建Mock对象,在一定程度上减少了创建Mock对象所带来的工作量。
2.EasyMock使用示例EasyMock的使用方法和原理的详细说明请参见"
EasyMock使用方法和原理剖析"
一文。
在这里,我们仅以HttpServletRequest为例对EasyMock的功能做简单说明。
在部署到Servlet容器之前,需要和HttpServletRequest进行交互的模块可以通过构建Mock对象的方式进行单元测试。
下面是使用EasyMock(version2.3)构建Mock对象进行简单测试的例子:
publicclassHttpServletRequestUtil{
publicstaticbooleanvalidate(HttpServletRequestrequest){
Stringhost=request.getHeader("
Host"
);
returnhost.startsWith("
"
}
publicclassHttpServletRequestTestCaseextendsTestCase{
publicvoidtestHttpSevletRequest(){
HttpServletRequestmockRequest=createMock(HttpServletRequest.class);
mockRequest.getHeader("
expectLastCall().andReturn("
:
80"
).times
(1);
replay(mockRequest);
assertTrue(HttpServletRequestUtil.validate(mockRequest));
verify(mockRequest);
首先,我们通过EasyMock提供的静态方法createMock创建Mock对象mockRequest。
当Mock对象创建好以后,我们就可以对Mock对象的预期行为和输出进行设定。
对预期行为和输出的设定分成两个部分:
(1)对指定方法进行调用;
(2)对预期输出进行设定。
在上例中,mockRequest.getHeader("
对Mock对象的getHeader方法进行了调用,之后用expectLastCall().andReturn("
).times
(1)对Mock对象的预期输出进行了设定。
andReturn方法设定了当getHeader方法被调用时,将返回字符串"
,times方法设定了该方法预期被调用的次数是1。
在结束对Mock对象预期行为和方法的设定之后,我们可以调用replay静态方法将mockRequest对象切换成回放状态。
在回放状态下,Mock对象的方法调用将返回预先设定的输出。
在上例中,HttpServletRequestUtil类的validate方法对mockRequest的getHeader方法进行了调用,并对得到的值进行验证。
最后,我们可以用verify方法来验证预期方法的调用是否真的完成了。
假如将上例中expectLastCall().andReturn("
).times
(1)设定的调用次数修改为2,而实际测试中只调用了一次该方法,您将会看到以下的错误:
通过示例,我们了解了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(如下图):
在XMLEasyMock中,我们选用XML文件作为Mock对象的配置文件,XML文件的自定义和结构特性使得它成为描述Mock对象最佳的选择。
根据以上对Mock对象信息配置的分析,我们可以给出Mock对象配置文件的模板:
?
xmlversion="
1.0"
encoding="
UTF-8"
mockConfigmockObjectsmockObjectname="
Objectname"
mockedClass="
Mockclassorinterface"
/
.
/mockObjectsmockBehaviorsmockBehaviormockObject="
method="
Expectedinvocationmethod"
paramValuesparamValuetype="
Parametertype"
value="
Parametervalue"
/paramValuesctrlOptionsctrlOptionoption="
Controloption"
Expectedreturnvalue"
times="
Expectedinvocationtimes"
…
/ctrlOptions
/mockBehavior
/mockBehaviors
/mockConfig
其中,mockObjects部分将配置Mock对象的生成信息,mockBehaviors部分将配置Mock对象的预期行为和输出。
印花税会计分录接下来,我们将对这两部分进行详细的说明。
根据配置文件生成Mock对象
配置文件中所包含的Mock对象生成信息包含在mockObject元素当中。
mockObject元素包含两个属性name和mockedClass,分别对应Mock对象的名称和对应的接口或类。
Mock对象的名称用于和配置文件中的其它部分相关联,而对应的接口和类用于Mock对象的生成。
ResultSet接口是每个Java开发人员都非常熟悉的接口。
以java.sql.ResultSet接口为例,为其生成一个Mock对象mockResultSet,可以在文件中配置为:
mockObjectname="
mockResultSet"
java.sql.ResultSet"
/我们可以设想一下,在EasyMock中,如果我们需要创建ResultSet接口的一个Mock对象,这个过程应当是:
IMocksControlmocksControl=EasyMock.createControl();
ResultSetmockResultSet=control.createMock(ResultSet.class);
其中,IMocksControl接口的实例mocksControl能生成并管理多个Mock对象。
在XMLEasyMock中,我们为每个Mock对象创建一个MockObject类的对象,同时用一个MockObjectController对象来管理这些Mock对象。
MockObjectController类拥有一个IMocksControl成员变量,同时提供了replay、个人所得税计算器verify和reset方法,供外部调用(如下图):
EasyMockUtil是提供给外部程序调用的工具类,loadConfig方法用于读取配置文件,findMockObjectByName方法可以通过Mock对象的变量名返回Mock对象。
配置Mock对象的预期行为
接下来我们需要配置的是Mock对象的预期行为。
Mock对象的预期行为可以简单的理解为是Mock对象方法的调用以及该方法的预期输出。
我们需要在文件中分别配置方法的预期调用和预期输出。
Mock对象的预期方法调用配置在mockBehavior元素中。
每个mockBehavior元素都包含两个属性:
mockObject和method属性。
mockObject指定该行为对应的Mock对象的名称(Mock对象必须在mockObject中定义过),method属性则指定Mock对象中预期调用的方法。
mockBehavior的子元素paramValues包含了需要配置的方法所对应的参数列表。
paramValues的每个子元素paramValue都包含两个属性:
type和value,分别指定了参数类型和参数值。
我们以ResultSet接口的Mock对象mockResultSet为例,如果我们期望对getString方法进行调用,可以配置以下信息:
在对Mock对象的方法调用进行配置以后,我们接下来对方法的预期输出进行配置。
方法的预期输出定义包含在ctrlOptions中。
ctrlOption中的option属性指定了MockControl对象在指定方法返回值时选用的选项。
Option属性可选的值包括:
其中,andReturn选项用于设定方法的预期返回值,当option属性为andReturn时,我们可以在value属性中配置方法的返回值。
在预期方法确定以后,其返回值类型也确定了,因此我们无需在此指定返回值类型。
times属性用于指定预定方法的调用次数。
如果希望为Mock对象方法设置默认的预期返回值,那么你可以选择andStubReturn,这时value属性中的返回值将作为预期方法的固定返回值,而无需多次设定。
andThrow选项用于设定预期异常抛出。
当option属性为andThrow时,value属性用于指定预期的异常类型。
times属性同样用于设定预期异常抛出的次数。
如果希望为Mock对象方法设定默认的异常抛出,您可以相应的选择andSubThrow。
如果预期方法的返回值为空(void),那么您应当指定andVoidCallable方法。
这时value属性不用设定(如果设定,XMLEasyMock会忽略该属性)。
我们仍然用ResultSet接口的getString方法为例,说明预期输出的配置效果:
以上的配置相当于在EasyMock中调用:
Myreturnvalue"
expectLastCall().andThrow(newSQLException()).times
(2);
XMLEasyMock中为每个Mock对象的预期行为创建一个MockBehavior对象。
MockBehavior类中包含了两个列表,分别包含了多个ParamValue对象和CtrlOption对象。
所有MockBehavior对象都由MockBehaviorController统一管理。
MockBehaviorController提供了loadMockBehaviors和runMockBehaviors方法,分别用于读入MockBehavior和执行预期行为设定。
这些类的关系如下图所示:
XMLEasyMock对Mock对象预期方法是通过类反射机制进行调用的。
如图4所示,当MockBehavior的runMockMethod方法被调用时,它首先通过Mock对象名查询Mock对象,接着从ParamValue中取出用户设定的参数类型和参数值。
根据Mock对象的类型、Mock对象的方法名和参数类型列表,我们可以通过Class的getDeclaredMethod获取到对应的Method对象。
最后,runModkMethod方法调用Method对象的invoke方法,完成Mock对象预期方法的调用。
在对预期方法进行调用之后,我们需要通过EasyMock类对方法的预期输出进行设定。
我们以设定预期返回值为例进行说明(设定预期异常抛出与此类似)。
如图5所示,MockBehavior类提供了runCtrlOptions用于设定方法的预期输出。
runCtrlOption方法首先调用之前得到的Method对象的getReturnType方法,获取方法的返回值类型,并将该返回值类型作为参数传递给CtrlOption的runCtrlOption方法。
runCtrlOption方法首先调用EasyMock类的expectLastCall静态方法,获得Mock对象所对应的IMocksControl实例,之后,根据预期方法的返回值类型对配置文件中的返回值进行格式化,将格式化后的数据作为参数传递给IMocksControl的andReturn方法,最后,调用times方法设定预期调用次数。
以上是XMLEasyMock对Mock对象生成、Mock对象预期行为设定的具体实现。
对于外部程序而言,只需要调用EasyMockUtil提供的loadConfig静态方法就可以达到根据配置文件构建Mock对象的目的了:
EasyMockUtil的loadConfig方法MockObjectController的loadMockObjects方法和MockBehaviorController的loadMockBehaviors方法读取和创建Mock对象及其预期行为。
MockBehaviorController的runMockBehaviors先后调用runMockMethod和runCtrlOptions方法设定Mock对象的预期方法调用和预期输出。
最后,loadConfig方法调用MockObjectControllerreplay方法将Mock对象切换成Replay状态。
4.利用Mock对象定义机制配置预期结果
在进行单元测试时,被测试模块的预期结果也编码在代码中,当测试数据或是测试用例发生变化时,预期结果也将发生改变。
我们是否能将预期结果也定义在配置文件中呢?
我们可以将被测试的对象在正确运行的情况下的行为抽象为一个Mock对象,它的预期输出,就是被测试对象在正确运行情况下的预期输出。
通过这种方式,我们就可以用类似配置Mock对象的方式对预期结果进行配置了。
在XMLEasyMock中我们提供了一个测试用的接口SalesOrder,它的实现类SalesOrderImpl的主要功能是从数据库中读取一个SalesOrder的Region和TotalPrice,并根据读取的数据计算该SalesOrder的PriceLevel:
如果我们对getPriceLevel方法进行测试,就可以将该方法在正确运行下的预期输出抽象成Mock对象,并配置如下:
mockSalesOrder"
xmleasymock.demo.test.SalesOrder"
getPriceLevel"
paramValues/
ctrlOptionsctrlOptionoption="
andReturn"
expectedresult1"
1"
ctrlOptionoption="
expectedresult2"
/mockConfig5.使用配置文件运行测试用例与在代码中动态构建Mock对象不同,XMLEasyMock是在配置文件的解析过程中动态生成Mock对象的。
因此,如果用户需要使用Mock对象,需要从解析模块中获取。
XMLEasyMock提供了一个工具类EasyMockUtil,这个工具类提供了findMockObjectByName方法用于返回Mock对象。
该方法的输入参数就是在配置文件的mockObject元素中配置的name属性。
另外,我们在上文中提到,EasyMockUtil提供了方法loadConfig用于装入配置文件,该方法的输入参数就是配置文件的路径。
在XMLEasyMock提供的测试代码(SalesOrderTestCase.java)中,我们对ResultSet接口进行了模拟,从而对SalesOrder的getPriceLevel进行测试。
在完成相关Mock对象的配置之后,我们可以通过XMLEasyMock提供的功能对测试用例实现如下:
publicclassSalesOrderTestCaseextendsTestCase{
publicvoidtestAfterConfig(){
try{
EasyMockUtil.loadConfig("
/xmleasymock/demo/properties/mockConfig.xml"
DBUtilitymockDBUtility=
(DBUtility)EasyMockUtil.findMockObjectByName("
mockDBUtility"
Connectionconn=mockDBUtility.getConnection();
Statementstmt=conn.createStatement();
ResultSetrs=stmt.executeQuery("
select*fromsales_order"
SalesOrderrealOrder=newSalesOrderImpl();
SalesOrdermockOrder=
(SalesOrder)EasyMockUtil.findMockObjectByName("
while(rs.next()){
realOrder.loadDataFromDB(rs);
assertEquals(realOrder.getPriceLevel(),mockOrder.getPriceLevel());
EasyMockUtil.verify();
}catch(Exceptione){
e.printStackTrace();
通常,在数据库连接模块中,开发人员都会开发一个类似于DBUtitlity的实现来获取数据库连接以及释放数据库资源。
在我们的测试用例中,这不是必需的,但为了对真实的开发和测试环境进行模拟,我们也在mockConfig.xml中对DBUtiltiy,Connection和Statement等接口进行了配置。
我们通过DBUtilityMock对象的名称"
获得mockDBUtility对象,并通过它得到ResultSet的Mock对象。
在对getPriceLevel方法进行测试时,我们将预期结果抽象成Mock对象,它在配置文件中的名称是mockSalesOrder。
我们通过这个名称获得预期结果的Mock对象。
最后,我们将实际结果和预期结果进行比对,从而完成测试。
6.结论
我们通过配置文件对Mock对象进行定义,实现了测试数据和代码的分离,从而避免了将数据编码在代码中所带来的一系列不便。
当测试数据或是测试用例发生变化时,开发或部署人员只需对配置文件作出改动,而不用修改测试代码和重新编译、部署,降低了测试用例发生变化所带来的工作量和时间花销。
本文基于EasyMock实现了通过配置文件定义Mock对象的机制。
初入淘宝,别让代理商毁了你的店铺
特别声明:
1:
资料来源于互联网,版权归属原作者
2:
资料内容属于网络意见,与本账号立场无关
3:
如有侵权,请告知,立即删除。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Mock 对象 创建高效灵活的测试用例 创建 高效 灵活 测试