求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
 
单元测试细节
 

2010-06-01 作者: h3w4 来源:h3w4的blog

 

功能测试

对于一个代码单元,首先要测试它的基本功能。功能就是在某种输入时应该产生某种确定的输出。对于一个代码单元,它的可能输入通常是无穷的,显然,把输入的 所有可能取值都进行测试,是不可能也是无意义的,我们应该用一定的规则选择有代表性的数据来建立测试用例。要考虑的输入主要有三种:正常输入,边界输入, 非法输入,每种输入还可以分类,也就是平常说的等价类法,每类取一个数据作为输入数据建立测试用例,如果测试通过,可以肯定同类的其他输入也是可以通过 的。如果等价类的划分是准确且完整的,并且每一个等价类都进行了测试,那么,可以说这个代码单元经过了充分的测试。下面举例说明:

正常输入

例如字符串的Trim函数,功能是将字符串前后的空格去除,那么正常的输入可以有四类:前面有空格;后面有空格;前后均有空格;前后均无空格。

边界输入

上例中空字符串可以看作是边界输入。

再如一个表示年龄的参数,它的有效范围是0-100,那么边界输入有两个:0和100。

非法输入

非法输入是正常取值范围以外的数据,或使代码不能完成正常功能的输入,如上例中表示年龄的参数,小于0或大于100都是非法输入,再如一个进行文件操作的函数,非法输入有这么几类:文件不存在;目录不存在;权限错误。

如果输入有多个数据,还要考虑这些数据之间的关系,即等价类还要包括数据的组合。

要人工找出所有的等价类通常是很困难的,但使用VU,通常不需要这样做,只要为容易想到的、比较典型的等价类建立测试用例就行了,后文会有进一步的论述。

从上述可以看出,测试用例编辑器可以适应很复杂的测试用例的编辑。只要一个测试用例已编辑完毕,第二个及更多的测试用例就很简单了:选定一个现有的测 试用例,由系统自动拷贝,用户再对部分数值进行修改就行了。由于各个测试用例之间的差别通常很小,这种方式可以很快建立测试用例集。

测试用例编辑器还具有代码模式,很特殊的情况下,可以切换到代码模式下直接编辑代码。

白盒测试

这里所说的白盒测试,是指针对程序的逻辑结构来设计测试用例进行测试。白盒测试用逻辑覆盖率来衡量测试的完整性。逻辑单位主要有:语句、分支、条件、条件 值、条件值组合,路径。语句覆盖就是覆盖所有的语句,其他类推。另外还有一种判定条件覆盖,其实是分支覆盖与条件覆盖的组合,在此不作讨论。跟条件有关的 覆盖就有三种,容易混淆,解释一下:条件覆盖是指覆盖所有的条件表达式,即所有的条件表达式都至少计算一次,不考虑计算结果;条件值覆盖是指覆盖条件的所 有可能取值,即每个条件的取真值和取假值都要至少计算一次;条件值组合覆盖是指覆盖所有条件取值的所有可能组合。循环结构一般不考虑循环的次数,而只考虑 至少执行循环体一次和不进入循环体两种情况,即把循环结构看作具有进入循环和不进入循环两个会支的分支结构。由于不同的覆盖率具有不同的侧重点,为了保证 充分的测试,最好是完成几种覆盖率的组合。VU选择了四种覆盖的组合作为白盒测试指标:语句覆盖、条件覆盖、分支覆盖、路径覆盖。如果这四种覆盖都达到了 100%,那么,测试的完整性是非常高的,读者可以比较一下,在使用VU之前,您的项目要求达到什么样的白盒覆盖?实际上又能完成到什么程度?

白盒测试与功能测试有什么关系呢?可以说,如果功能测试是足够充分的,那么白盒测试就没有必要,从等价类的角度来看,足够充分是指,等价类的划分是完 全正确的,并且所有的等价类都测试到了。如何衡量功能测试的完整性呢?人工审核是一个方法,不过这种方式代价较高,并且仍然不能保证结果的准确性。难于衡 量测试的完整性是功能测试的主要缺陷,而白盒测试恰恰具有易于衡量测试完整性的优点,两者之间具有极好的互补性。

在单元测试中,功能测试和白盒测试通常具有这样的关系:如果功能测试足够充分,那么,所有可以覆盖的白盒逻辑单位都已经覆盖,剩下的是不可覆盖的;反 过来说,如果有一些逻辑单位未覆盖,并且不能证明该逻辑单位是不可覆盖的,那么,可以认为功能测试不完整:或者等价类的划分不够准确完整,或者有些等价类 未测试到。也就是说,白盒覆盖指标可以衡量功能测试的完整性,依据某个未覆盖的逻辑目标,可以发现遗漏的等价类。运行测试后,VU会自动统计白盒覆盖状 况,标示出未覆盖的代码、条件、分支及路径,并且提供测试用例设计器来帮助设计可以覆盖遗漏的逻辑单位的测试用例。只要有了第一个测试用例,测试用例设计 器就能很好地工作。

前面说过,使用VU,能达到100%语句、条件、分支、路径覆盖。很多读者会问,对于稍为复杂一点的程序,路径就可能几百上千条,100%覆盖可能吗?那要耗费多少资源?

任何程序,不管它有多复杂,都不可能脱离客观规律和客观实际。路径是什么?路径是代码的组合,就是程序可能的运行路线。对于一个函数来说,只要输入确 定了,程序的运行路线就确定了(局部静态变量是一个特例,它实质上是全局变量,只不过是局部可见而已,因此,也应该看作是一个输入)。从等价类的角度来 看,一个等价类里所有可能取值,一般来说覆盖的路径是一样的。我们可以大体上认为:程序的可覆盖路径的数量与等价类的数量基本相等。例如,一个程序,功能 是输入人民币小写数值,输出大写字符串,它的路径可能有几百条,但是,我们简单的想一下,就会发现等价类不会超过十个,因此,大体上可以说,可以覆盖的路 径不会超过10条。

至于语句、条件、分支,也可能有一些是不能覆盖的,这些逻辑目标的处理要比路径简单得多。我们说达到100%语句、条件、分支、路径覆盖,是指覆盖可以覆盖的部分,并把不能覆盖的部分识别出来并作适当的处理。

VU的测试用例设计器具有两方面的功能:识别不可覆盖的逻辑目标,为可覆盖的逻辑目标设计测试用例。测试用例设计器的工作原理是:从现有的测试用例中 计算出一个最接近于可覆盖预期逻辑目标的用例作为近似用例,并生成修改提示,依据修改提示对近似测试用例的一个或多个输入数据(通常是一个)进行修改,并 视需要修改预期输出,即可获得可以覆盖预期逻辑目标的测试用例。修改提示的主要内容是已满足条件和待满足条件,已满足条件是近似测试用例已经满足的条件, 修改近似测试用例时要保证不破坏已满足条件,待满足条件是新的测试用例必须满足的条件。如果待满足条件与已满足条件不冲突,则依据待满足条件修改近似测试 用例获得新的测试用例,如果冲突,则该逻辑目标是不可覆盖的,如果条件提示难于理解,可以切换到代码模式下,查看程序逻辑。

不可覆盖的语句或条件,一般属于冗余代码,建议删除。不可覆盖的分支或路径,则在逻辑结构图中打上删除标识,这种删除不会影响代码。

有读者会问,如果几百上千条路径,要一条一条识别它能不能覆盖,那可能吗?这种担心是不必要的。只要按照语句覆盖、条件覆盖、分支覆盖、路径覆盖的顺 序来完成逻辑覆盖,工作量通常都很小,原因是可确认的不可覆盖的路径会自动剔除。例如,如果一条分支不可覆盖,那么所有“经过”这条分支的路径也肯定不可 覆盖,把一条分支删除了,所有“经过”该分支的路径都会自动删除,所以,在完成语句、条件、分支覆盖后(包括把不可覆盖的分支删除),路径数量就不会是庞 大的了,未覆盖路径则更少。

对于复杂的程序,还可以在逻辑结构图中屏蔽一些安全的分支结构。有一些代码形成的分支结构,对其后的程序的逻辑并无影响。例如:
void Find(CString& str, char ch, int start=0, int end=-1)
{
if(end == -1)
end = str.GetLength();
......
}

上列的if结构形成的分支结构只用于处理缺省值参数,对后面的代码逻辑无影响,因此是安全的。在逻辑结构图中,与一个复杂的分支结构并列的一个或多个简单 的分支结构,通常都是安全的,可以在逻辑结构图中查看各个分支结构对应的代码以判断该分支结构是否安全。安全的分支结构可以屏蔽,以便把注意力集中到值得 关注的部分。屏蔽安全的分支结构也会使路径的数量大幅度减少。前面所说的人民币小写转大小的程序,在删除了不可覆盖分支,屏蔽了安全的分支结构后,路径只 剩下不到十条,与等价类的数量是接近的。

边界测试

在测试用例部分已经说过,设计测试用例时要考虑边界输入和非法输入,这里统称为特殊输入,程序员在编写代码时也要考虑特殊输入,要为特殊输入编写处理代 码。在实际工作中,程序员没有考虑到某些特殊输入是很常见的,这也是程序错误的一个重要来源。不幸的是,如果编写代码和建立测试用例时都没有考虑这些输 入,那么白盒覆盖也不能自动发现,因为白盒覆盖是以代码为基础的,如果相应的代码根本不存在,白盒覆盖当然不会告诉用户“某某代码未覆盖”。

特殊输入通常与数据类型有关,例如,如果一个参数是指针,空指针就是一个特殊输入,对于一个整数类型,最大值、最小值、0、1、-1都可以算是特殊输 入;另一方面,当输入特殊数据时,如果程序未作合适的处理,运行结果常常是产生异常。根据这两个条件,如果预先为各个数据类型定义特殊值,然后由测试工具 自动生成测试用例,使用这些特殊值或其组合作为输入数据进行测试,通常可以发现“未处理特殊输入”而产生的程序错误,这就是边界测试。

如果在边界测试中发现了处理某些特殊输入的代码缺失,应补充这些代码,并完成白盒覆盖。

为了进一步说明测试完整性,举个例子:假如一个函数,大概有十个等价类,但这个数字并不是显式的,测试人员比较容易想到的,可能只有五个,自己画一下逻辑 结构图,做一些代码分析,也许又可以找到二三个,是不是齐全了呢?很难衡量。使用VU,设计测试用例的过程大体是这样的:用户为容易想到的五个等价类建立 测试用例,根据语句、条件、分支覆盖,在测试用例设计器的帮助下,找到另两个等价类,根据路径覆盖,又揪出了两个很隐秘的等价类,实现了100%语句、条 件、分支、路径覆盖,最后,边界测试又发现了一个意料之外的等价类。当然,这些数字只是一个示意,实现某某覆盖的目的,并不在于覆盖率,而在于尽可能找出 所有等价类。

如果针对代码单元的测试完成了100%语句、条件、分支、路径覆盖,并且执行了边界测试,虽然仍然不能证明所有的等价类都测试到了,但可以肯定的说, 已经达到了很高的测试完整性,局部代码中仍然含有错误的可能性很小,当然,设计上的错误除外,这不属于单元测试的范畴。

回归测试

在修改了代码后,可以使用回归测试,对选定的任何类或整个工程执行测试,以检测修改是否破坏了现有代码的功能。一般来说,修改了私有函数,要运行整个类的测试,修改了公有或保护函数,则要运行整个工程的测试。



LoadRunner性能测试基础
软件测试结果分析和质量报告
面向对象软件测试技术研究
设计测试用例的四条原则
功能测试中故障模型的建立
性能测试综述
更多...   


性能测试方法与技术
测试过程与团队管理
LoadRunner进行性能测试
WEB应用的软件测试
手机软件测试
白盒测试方法与技术


某博彩行业 数据库自动化测试
IT服务商 Web安全测试
IT服务商 自动化测试框架
海航股份 单元测试、重构
测试需求分析与测试用例分析
互联网web测试方法与实践
基于Selenium的Web自动化测试
更多...