基于模拟对象Mock Object的单元测试研究

来源 :电脑知识与技术·学术交流 | 被引量 : 0次 | 上传用户:Lossed
下载到本地 , 更方便阅读
声明 : 本文档内容版权归属内容提供方 , 如果您对本文有版权争议 , 可与客服联系进行内容授权或下架
论文部分内容阅读
  摘要:在软件测试中,单元测试不仅能够优化软件系统设计,还大大简化了功能测试的测试量。但是在一些情况下进行单元测试比较困难,本文引入了模拟对象Mock Object的概念,利用Mock Object进行单位测试,解决了传统单元测试中存在的一些问题。
  关键词:软件测试;单元测试;模拟对象
  中图分类号:TP311文献标识码:A文章编号:1009-3044(2008)05-00ppp-0c
  
  1 引言
  
  随着极限编程在实际软件开发项目中的推广,越来越多的项目开始采用测试驱动开发作为主要的软件开发方法。单元测试不仅优化了软件系统设计,还大大简化了功能测试的工作量[1]。但是另一方面.更多的项目在开始不久就发现在很多情况下针对一个类编写单元测试比较困难.随着项目的进行,越来越多的代码无法进行单元测试.到最后整个项目无法继续采用测试驱动的方式进行开发。因此,要将测试驱动开发真正在整个项目里贯彻执行,必须有一种方法能够相对容易的解决这些问题。本文将首先讨论了单元测试和无法或很难进行单元测试的情况,然后引入Mock Object的概念,基于Mock Object实现单元测试。接下来讨论在软件开发过程中引入Mock Object对测试和设计的影响。最后简述了Mock Object的局限性。
  
  2 单元测试
  
  2.1 什么是单元测试
  单元测试是对程序中的单个子程序或过程进行测试的过程,也就是说,一开始并不是对整个程序进行测试,而是将注意力集中在对构成程序的较小模块的测试上面[2]。单元测试从两个角度进行测试:一是测试数据都是针对程序的功能来设计的黑盒测试;二是针对程序的逻辑结构来设计测试用例的白盒测试。
  2.2 单元测试面对的难题
  造成针对一个类难以进行单元测试的主要原因是因为这个类依赖于一些其它的难以测试的资源。主要有这三类最主要的资源:数据库,第三方组件和网络硬件资源。下面我们将对这三大类难以测试的资源进行分类讨论。
  2.2.1 数据库
  现在大部分的软件项目都会采用数据库作为数据存储。常见的开发团队会在每个开发人员的机器上安装一个本地的数据库,每个人针对自己的数据库进行开发调试。这样做的问题是:必须有一种方式同步数据库的设计。如果有一个人修改了数据库schema或者某个存储过程,这个修改必须同步到所有开发者的本地数据库以及测试服务器上。采用敏捷软件开发的很多项目组往往会浪费大量的时间在数据库设计同步上。更严重的是每周都会遇到由于数据库设计不同步,修改冲突导致的问题导致整个项目的中心源码库在Auto Build时失败。每个开发人员都有自己的测试数据,除了上面提到的需要把这些测试数据同步到所有开发机器和测试服务器上外,还面临更重大的问题。因为测试用例需要修改数据库,因此还必须准备一种机制能够在每一个测试用例执行结束后重新将所有的测试数据调入数据库。采用最简单直接的方法就是在每个测试用例执行前都将数据库清空,然后再将测试数据调入,这样会大大减慢单元测试的时间。单元测试时间越长,开发者就越不愿意执行这些测试用例,单元测试所发挥的作用越小,这也是很多测试驱动项目最终无法进行到底的一个重要原因。另一个非常严重的问题是为了清理测试环境,在针对商业逻辑的测试用例中加入了大量的数据访问层的代码。采用这样的方式强迫开发者在开发商业逻辑层的同时开发数据访问层,并且严重降低了可读性。
  2.2.2 第三方组件或应用服务器
  数据库是最常见的第三方服务器。除此以外在越来越多的项目中使用第三方的组件和应用服务器。例如:客户环境中的ERP系统,全球定位系统(GPS)的Web Service接口,绘图引擎等。对于这些第三方提供的内容,造成难以编写单元测试的最根本的原因有:一是系统不透明:对于大部分商业组件或者服务来说,一个很重要的内容是良好的封装。但这个特性带来的问题是在外界无法对其内部状态进行控制和访问。往往经过好几个操作后才能在外部观察到相应的变化。二是环境配置困难。由于项目组成员计算机配置不同,加入项目的时间不同,在项目中负责的内容不同导致无法为所有开发人员配置一个完全一致的环境。例如一个绘图引擎的开发版的license是按照一个局域网内部同时使用的人员个数收费的,就不可能只为了能够进行完整的单元测试就为只编写商业逻辑层的开发人员也安装一套。
  2.2.3 网络资源和硬件资源
  在稍大一些的项目中都或多或少的用到一些网络资源。例如将文件部署到远程的webDAV服务器上同时很多项目还会用到一些硬件资源。常见的有打印机、指纹识别验证或者条形码阅读器等。这些资源有两大特点导致很难针对与他们相关的类编写测试用例。
  一是资源访问冲突。很多网络资源对于并发访问的响应协调是通过锁机制进行的,在实际项目中常见的是一个开发人员在调试本地代码时导致远端资源被锁定导致其它开发者无法访问这些资源。
  二是环境可控因素。对于网络资源和硬件资源相关代码的测试与针对商业逻辑层代码的测试最大的不同是环境的不确定性。访问网络资源有可能遇到的异常情况非常多,例如网络忙造成访问超时,也有可能建立链接后数据传输失败,还有可能数据传输完成后校验失败。针对访问这些资源的代码进行的测试必须能够覆盖到所有可能出现的每一种情况。如果没有一个可控,并且是全自动的环境辅助单元测试的话,这项任务基本上不可能完成。
  
  3 模拟对象
  
  3.1 什么是模拟对象
  Mock这个单词翻译成中文大概的意思是假的,模拟的。如图1所示:通过一个常见的对商业逻辑的测试描述了一个Mock Object。在图中我们可以看出:测试代码需要测试商业逻辑,而商业逻辑代码需要通过IMyDataAccess接口访问底层数据库,这就是数据库依赖问题。为了解决这个问题我们引入一个Mock Object,并将这个Mock Object而非真正的Data Access传递给商业逻辑代码进行测试。这里的Mock Object不需要实现任何逻辑只需要根据商业逻辑的需要返回适当的内容就可以了。
  
  图1 使用Mock Object对商业逻辑进行测试
  
  3.2 模拟对象实现单元测试应用实例
  现在我们写好了类AccountService,具体如下:
  public class AccountService {
  private AccountManager accountManager;
  public void setAccountManager(AccountManager manager) {
  this.accountManager = manager;
  }
  public void transfer(String senderId, String beneficiaryId, long amount) {
  Account sender = this.accountManager.findAccountForUser(senderId);
  Account beneficiary =
  this.accountManager.findAccountForUser(beneficiaryId);
  sender.debit(amount);
  beneficiary.credit(amount);
  this.accountManager.updateAccount(sender);
  this.accountManager.updateAccount(beneficiary);
  }}
  现在我们想测试transfer方法,它内部调用的AccountManager的两个方法。但是对于AccountManager来说,它只是个接口,如下:
  public interface AccountManager {
  Account findAccountForUser(String userId);
  void updateAccount(Account account);
  }
  所以现在我们必须写个MockAccountManager对象。而且里面的方法体都是非常简单的,就是假定它就返回某某值。
  我们这里还有Account类。
  public class Account {
  private String accountId;
  private long balance;
  public Account(String accountId, long initialBalance) {
  this.accountId = accountId;
  this.balance = initialBalance;
  }public void debit(long amount) {
  this.balance -= amount;
  }
  public void credit(long amount) {
  this.balance = amount;
  }
  public long getBalance() {
  return this.balance;
  }
  public String getAccountId() {
  return accountId;
  }}
  public class AccountService1Tests extends TestCase {
  public void testTransfer(){
  AccountService as = new AccountService();
  MockAccountManager mockAccountManager=new MockAccountManager();
  Account accountA = new Account("A",3000);
  Account accountB = new Account("B",2000);
  mockAccountManager.addAccount(accountA);
  mockAccountManager.addAccount(accountB);
  as.setAccountManager(mockAccountManager);
  as.transfer("A","B",1005);
  assertEquals(accountA.getBalance(),1995);
  assertEquals(accountB.getBalance(),3005);
  }}
  这里我们在假定AccountManager方法都工作正常的情况下,完成了对transfer方法的测试。
  从以上代码可以看出,采用Mock Object进行的单元测试基本上可以分为下面几步:
  (1)基于一个接口定义Mock并实现这个接口的所有函数。
  (2)创建Mock Object的一个对象
  (3)设置对象内部属性
  (4)告诉对象测试代码希望看到的反应
  (5)进行测试
  (6)检查Mock Object的确按照希望的顺序进行工作。
  3.3 模拟对象的优点
  3.3.1 模拟对象作为测试手段的优点
  Mock Object最直接的优点在于提供单元测试的质量和覆盖率:
  (1)只要在测试中对期待发生的问题指定好执行的顺序引入Mock Object对象后的单元测试就是在一个完全可控的环境里进行的。也就是说我们再也不会无法定位一个“时隐时现”的bug。相反我们可以非常迅速的将问题定位在一个类的内部,而不是一个函数调用序列。
  (2)于测试人员来说,最常见的问题是测试人员提交的bug无法在开发人员那里复现。有了Mock Object这个工具测试人员可以利用Mock Object明确的指定输入和输出编写一个测试用例让开发人员修复。
  (3)超过8O% 的异常处理代码没有被充分测试过。主要原因是在没有Mock Object之前很多情况是无法由人工进行控制的,例如写文件失败网络连接超时,数据库数据传输失败或者从网络接收到的数据已经损坏。通过控制Mock Object我们很容易就可以模拟上面的这些情况。
  3.3.2 模拟对象作为设计手段的优点
  虽然Mock Object最直接的优点在于给予测试代码更多的可控性和可操作性,它最大的优点在于对软件设计的影响[3]。
  (1)测试驱动开发与Mock Object一起使用,可以写出低耦合高内聚,非常优雅干净的代码。
  (2)强迫设计者放弃对第三方库的强依赖关系,取而代之的是比较弱的依赖关系。
  (3)设计人员可以将更大的注意力放在商业逻辑的实现和测试.由于Mock Object的存在,我们不需要实现数据访问层就可以对商业逻辑进行测试。而商业逻辑才是任何系统中对于客户最重要的内容,它的正确与否决定了整个系统是否能完成任务,它的稳定性决定了整个系统架构的稳定性。
  (4)在项目初期,甚至是中期,将设计人员解放出来,不用对系统底层的基础设施做出判断。例如,在商业逻辑并不明确,需求还不稳定的时候,我们是更多根据感觉来做出很多重要的判断的,而这些判断往往导致比较关键的决定。例如,在项目之初,谁能够明确的回答到底需要什么样的数据库?Oracle?SQL Server?还是XML文件?到底需要什么样的队列服务器 MSMQ还是IBM—MQ?由于Mock Object的引入,我们可以将这些决策推迟到商业逻辑层更加明确之后进行,从而可以获得更加准确有针对性的答案。
  3.4 模拟对象的局限性
  Mock Object在实际项目中的应用存在一些限制,一些是由于Mock Object本身性质决定的,有一些则是由于其它类库设计存在的缺陷导致的。
  (1)一个典型的不是Mock Object的问题,而是类库设计的问题。是Mock Object无法模拟比较深的对象树。有一些第三方的类库,尤其是一些消息处理函数的参数,提供的不是接口而是一些对象。往往这些对象内部有很多子对象,也就是我们常说的一棵大的对象树。我们需要花费太多的精力去构造这些对象来进行模拟,时间消耗巨大。
  (2)一般性而言,单元测试的粒度越细,功能测试的粒度就可以越粗[4]。但是引入Mock Object的单元测试仍然无法取代功能测试。一个很好的例子就是误差积累的测试,哪怕每个单元的误差都在可接收范围内,我们仍然需要一个功能测试确保整体误差也是可以接受的。
  
  4 结束语
  
  模拟对象解决了传统单元测试的两个问题:一是如何将需要测试的代码与相关环境隔离;二是如何创建一个快速、可控的环境辅助测试开发。随着模拟对象技术的成熟,基于模拟对象的单元测试会越来越广泛地被采用。
  
  参考文献:
  [1]Kent Beck.测试驱动开发[M].北京:中国电力出版社,2003.
  [2]Myers.王峰,陈杰译.软件测试的艺术(第二版)[M]. 北京:机械工业出版社,2006.50-52.
  [3]David Astels.崔凯,译.测试驱动开发实用指南[M].北京:中国电力出版社,2004.120-130.
  [4]Paul C Jorgensen.韩柯,杜旭涛,译.软件测试(第二版)[M].北京:机械工业出版社,2003.
  
  收稿日期:2007-12-15
  作者简介:刘赟(1980-),男,硕士,研究方向:软件工程。
其他文献
食品安全追溯如何实现?  ●肉能追溯到屠宰场,菜最远只能到批发市场  ●短期允许“大体系、小农商”的二元市场存在  ●各部门“分段监管”,再现“九龙治水”之忧  假羊肉、镉大米,时至年末,回忆2013年发生的一次次食品安全风波,仍觉惊心。  对于国人而言,食品安全已是老生常谈,但监控食品保障安全的政策规定不断推出,消费者对食品安全的信心却越来越低。  如何监控食品安全才能达到最佳效果?国际食品法典
摘要:该文介绍了一个在ORM层面使用EntityFramework的倉库管理系统的设计方案。在表示层使用了ASP.NETMVC5,这个也是ASP.NETMVC Core之前的最后一个版本。系统利用了EntityFramwork的一些特性,基于Lambda表达式设计了一些通用的函数,极大地提升了系统的可重用性和可拓展性。基于这套底层框架,以后可以应对各种不同的需求进行快速开发。  关键词:Entit
摘要:随着计算机科学技术的日新月异,绘图设计已成为计算机科学技术应用的一个重要的方面。在此,分别对三个有代表性的绘图软件AutoCAD、 3DS Max及Photoshop的特色、實际应用及综合应用的流程作以研究,有利于读者综合掌握所学的绘图知识,并能实际应用,以此进一步促进三软件的综合应用的教学与实践。  关键词: AutoCAD;3DS;Photoshop;综合应用  中图分类号:TP393
摘 要:高考压轴题的知识点源于教材,有时难度会高于教材。通过挖掘教材,从人教版教材“功能关系”内容中总结出10个物理模型是此研究的重要工作。物理模型不仅能帮助学生解题,还能帮助教师编题,解决了许多教师“编题难”的问题,从而达到高效备战压轴题的目的。  关键词:高考物理;压轴题;教材;模型  中图分类号:G633.7 文献标识码:A 文章编号:1003-6148(2019)11-0040-3  
摘 要:文章简要阐述了“STEM 创客教育”与高中物理教学融合的缘由,通过文献分析与教学实践,提出了高中物理3D实验项目设计的策略和教学方法,并结合师生共同设计和开发“3D过山车模型”教具的案例,简要阐述了如何设计、开发3D物理课程资源,探讨了开发中遇到的问题和解决方法。希望可以抛砖引玉,引起更多的研究者关注并加入到3D物理课程资源开发与设计的研究中。  关键词:3D打印;物理模型;3D物理教具
吊炉花生  工作日常有,而假期不常有。五一小长假又要来了,你是不是已经按照“放假攻略”提示的那样,采用“3 2”请假战略,把三天小长假变成了七天黄金周呢?  如果你选择利用假期来一趟出境游,那可一定要利用好“过境免签”这项政策,拿着好不容易办下来的出境签证,不去第三国或地方走一圈,岂不是辜负了这大好春光?  看不够的“江南Style”  为了吸引游客,很多旅游热门的国家和城市都在利用“过境免签”政
【摘 要】书法是一门以书写汉字为表现形式的艺术,与人们的日常生活息息相关。近些年来,书法教育蓬勃发展,但是在教学中,人们往往忽视了书法用笔的问题。由此,我们应当将教学思路由静态向动态进行转变,通过笔画和笔顺来复现汉字书写的动态过程,挖掘各个笔画间的内在联系,并落实到书法教学中,让学生真正地会写字,写好字。  【关键词】中小学书法教育;用笔;笔画和笔顺;运行曲线  【中图分类号】J292.1 【文献
摘 要:类比建模是物理学习的一种重要方法,特别在对物理概念的建构上起到很重要的作用,而猜想是物理探究实验中必不可少的环节。如何正确引导学生进行“有效”猜想,笔者从“电功”案例入手,通过类比建模,让学生在物理情境中理清本应在高中才掌握的知识——电流做功的微观过程,在启发学生猜想的同时也完成了对电功概念的初步建立,为后面的探究验证打下良好的基础,也为后续电热的学习做了铺垫。  关键词:类比建模;猜想
摘 要:实验是物理课程培养学生核心素养的重要途径,教科书中的实验是最基本的实验资源。以2019年版人教版和粤教版高中物理必修第一册教科书中的实验为研究对象,详细比较两版教科书在实验数量、呈现方式以及内容选择和处理上的差异,旨在为教师使用新教科书的实验资源提供参考。研究发现,在实验数量上,粤教版的实验较人教版丰富;在实验的呈现上,两版教科书均设有固定的栏目,呈现不同类型的实验,粤教版在实验的编排上侧
摘要:传统的网站只是在页面上使用计数器进行简单的访问者数量的统计。随着网站运营规模的扩大,依靠这种简单的数量统计并不能真正对网站运营状况做以全面的评估,难以为网站运营提供有意义的参考;基于Web的网站访问流量统计系统在功能上强化了对用户行为的统计和分析,有利于网站管理者、开发者根据目标客户使用网站的实际情况制定网站经营战略,调整网站运营架构,进而对网站整体进行更有意义的改进。  关键词: 流量分析