代码改变世界

白盒测试总结

2010-08-04 22:05  jiva  阅读(562)  评论(2编辑  收藏  举报

背景简述:

     今年三四月份的时候,在经历了几次框架的迁移,对自己负责的那一块业务内容的代码已经无比的熟悉和厌恶,而测试的自动化测试工具写的测试场景十分简单,测试周期又在一个星期在两个星期之间,考虑到每次框架迁移带来的BUG和测试的麻烦,催生了写单元测试进行自测的想法。

      最初的想法是写单元测试来着,后来断断续续的写着,行了若干次重构,等到全部完成的时候发现已经不是单元测试了,而像是白盒测试。不管白盒黑盒,捉住BUG就是好Test。总共写了将近一百个TestCase,覆盖率在90%以上。

 

产品技术和业务简介

       产品技术:

        公司的这款产品是常见的MIS系统,属于某行业的ERP性质的产品。从2000年左右开发,已经换代了三四次,开发语言是Delphi,自然没有NHibernate之类的ORM的持久化框架,对数据的操作和呈现没有采用ADO那一套控件,也没有采用Delphi下常见的DataSet系列控件,而是基于公司自主开发一套内存数据库系统以及基于该数据库的数据操作和呈现控件。公司的这套内存数据库系统和ADO没有太大区别,提供了DataBase, Table, Field等常见的访问方式,在系统的框架中,起到了类似ORM的作用,获取数据后通过数据敏感控件呈现,对数据操作后记录相应的Log,并将该LOG翻译成底层数据库的对应SQL。底层数据库支持多种,SQL Server, Oracle等。

        公司的产品同时支持C\S、B\S架构。

     业务简介

       本人负责的这一块业务就不说是什么,行业特性挺强的。说一下特点,也是这几点让我有做测试的驱动。主要特定有: 算法稳定、算法复杂、因此自测也非常麻烦。

 

测试范围

       对于一个MIS系统来说,测试的范围是非常多,界面相关的、数据相关的、约束相关的、还有算法等。看了一下项目组TD库中的BUG后,分析了一下出现BUG比较多的是项目组内称之为“二级窗体” 的地方和费用算法的地方,因此把这次想做的测试重点就放在了这两块中。”二级窗体“是提供选择数据的弹出窗体,类似于Domain Model中提到Repository。系统的算法存在的地方很多,可能是一些查询模块,也可能是一些费用计算等,费用计算的结果记录集基本上保存在弹出的二级窗体里。

     二级窗体中的获取数据的代码主要是公司内部的开发语言GQL

 

测试工具:

      Delphi下只有DUnit。  在Sourcef上转的时候发现程序员在开发DUnit的下一个版本DUnit,并且于09年10月份放出来了一个版本。DUnit的9.3,9.4的版本大概还是06完成开发的,考虑到DUnit2提供的Project支持,优化TestCase执行效率,报错信息优化,配置文件改善等措施,最终采用了DUnit2.

     另外: 无论DUnit还是DUnit2都存在一个很差劲的BUG。就是Win + D 之后,再也无法还原了。

 

构造数据:

      对二级窗体和算法进行测试首先需要构造数据,没有数据怎么验证获取的数据是否正确呢?因此需要考虑如何构造数据。总结了一下几种方式。

     1. 手动录制。。这没有办法写代码

     2. 采用Record方式,提前录制好,到时候再加载相应的文件。

     3. 构造相应的数据文件, 比如Excel, 利用测试工具加载该Excel,然后执行相应的测试。这应该算是很多DBTest采用的方式,简单的看了一下JAVA平台的几个DBTest工具和案例,感觉不是很适合。风险主要是数据库升级,多数据库支持,维护成本很大。

     4. 采用自动化工具生成。这是项目组测试们采用的办法,不过自动化工具太依赖于界面了,也很臃肿,就没有考虑。

     5. 调用系统模块代码,直接生成相应的数据。虽然没有采用OO,ORM之类的模式,但是系统的代码还是比较简洁的,可以直接调用相应的数据模块类来生成数据,做了几个Demo后采用了这种方式。后来回过头来这种方式对数据构造来说是非常合适的。

    总结一下构造数据需要满足的几点要求:

    1. 是否支持多数据库。这个根据系统的要求来做,如果系统确定支持一种数据库,那就无所谓了。

    2. 系统数据库的Schema是否经常改变的。数据库Schema的经常改变对于Test的维护影响还是巨大的。

    3. 数据生成的过程中是否考虑了比较完成的业务逻辑和系统逻辑。比如生成数据的时候是否会生成一些默认的系统表、填充一些默认的系统字段等、在修改某些字段或者填充某些记录后,是否会自动生成某些记录,获取记录或者保存记录的时候是否会对记录进行额外的处理。

    4. 对数据的操作是否有个中间层。比如本公司的内存数据库或者ADO,Delphi下的DataSet之类,这些能够保证数据访问操作等发生改变后,Test不会修改太大,维护起来很麻烦。

    5. 采用系统代码生成数据还有一个好处就是灵活。可以生成任意数据,任意组合。

    对于ERP之类行业MIS来说,采用系统的模块代码来生成相应的数据记录是最合适的。

    这个其实也说明了虽然系统不是OO的,但是只要是封装良好,还是容易进行外部调用的。

   此外: 还需要规划一下在TestCase中使用到的数据常量,比如说单价、数量等。

 

如何测试

    测试原则

     由于不是专业测试,在如何提取测试上一度非常难过这一部分是根据单元测试指南以及一些测试人员的指南,整理了一下测试原则:

      1. 测试路径无遗漏

      2. 测试路径无重复。

      3. 测试流程和测试数据分离。

 

   提取测试路径:

      提取测试路径其实是一项很有意思的活,之前没有想到测试这么麻烦,最初的想法是采取黑盒类似的穷举法,在尝试了几个TestCase之后,发现这一条能把人累死的路,还好我们是开发人员,能够看到代码,因此也就从代码中琢磨了几条方法。主要有以下:

      1. 列出来受影响的实体。受影响的实体决定了如何构造数据以及如何确定边界条件,由此可以分析出来边界点。相当于SQL中的From, 以及Where中的 In, Existing等。

      2. 根据字段取值来决定边界条件。虽然能够确定受影响的模块以及由此推导出测试边界点,但是可能复杂度还是非常的高,因此可以根据字段取值来进一步缩小边界条件。这也算是对SQL写的获取数据代码进行测试和对代码中IFELSE语句等进行测试的区别。相当于SQL中Select。

      3. 根据状态图或者流程图来提取测试路径。有些比较复杂的算法,可以考虑分析一下状态图或者流程图,根据这两个图可以分析出来最短的测试路径。

  

    另外: 对于SQL写的获取数据的代码进行测试除了SQL中Select , From, Where应该算是重头,对于Where中的条件也不是说每个条件都是需要测试的,考虑到测试的重要性,应该只测试那些对算法正确性有影响的Where条件,而对选择出来的数据进行再次过滤的where条件可以不予考虑。

 

    测试验证

       如何对获取出来的数据进行验证,尤其是SQL选择出来的RecordList的结果验证呢?一开始的时候也是非常的头疼,比如说考虑每个字段的取值、考虑排序了、考虑记录数之类的,后来总结了一下,只需要考虑一下三点:

       1. 记录数。直接决定了选择出来的记录尤其是SQL的执行结果是否正确。

       2. 字段取值。验证获取的数据的主键或者唯一标识是否正确;验证存在算法的字段是否正确。

       3. 记录顺序。验证记录是否符合某种顺序。

    

        这一部分其实是针对SQL中Select,Group by ,order by ,top 运算符来说的。

 

   测试感想

       虽然在06的时候开始接触UnitTest,由于一直在从事数据库相关程序的开发,认为比较适合做测试的代码最起码应该是OO,同时采用ORM框架的,经过这次,改变了很多看法。认为一个程序只要具备以下几点就可以考虑采用测试:

       1. 业务逻辑独立。

          对于客户端来说,业务逻辑不要和UI等耦合紧密,不要再Action或者Click中写业务逻辑,如果某些操作中存在对UI的操作,比如说不可避免的要设计到对话框的提示,此时应该考虑支持不弹出此对话框。对于服务端来说,如果客户端能够提供较好的调用机制,那么服务器端的逻辑是否和webservice, DataStore耦合在一起无所谓。

       2. 中间层隔离数据存储。

            提供一个类似ADOTable或者TDataSet之类的对数据操作的中间层,隔离框架的升级或者数据存储的升级。

       3. 业务逻辑类或者代码要提供良好的数据操作接口。

            比如对数据的集合操作、字段访问、记录增删改查等。

       4. Application的Mock。 提供一个较好的Application Mock, 能够方便的调用程序启动,执行各种操作。比如在这里避免模态的登录框等出现,避免主窗体的出现等。