这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 综合技术 » [转帖]单元测试简介

共33条 2/4 1 2 3 4 跳转至
菜鸟
2006-12-28 02:11:00     打赏
11楼
对象状态与操作

在面向对象的开发和测试中还涉及到对象的状态。一个成员函数,它的功能常常是修改了对象的状态,即在某种前置状态下执行该函数,其结果是使对象的状态发生了变化,即后置状态。测试时要设定前置状态,并判断后置状态是否符合预期

"对象的状态"是什么?其实就是成员变量的值,"设定前置状态,并判断后置状态是否符合预期"就是在输入数据部分给成员变量设定初始值,在预期输出部分判断成员变量的结果值是否符合预期





测试对象可能有很多成员变量,但对于一个成员函数来说,涉及到的成员变量一般不会很多。只需要为被测试函数需要读取的成员变量设定初始值,不必考虑其他无
关的成员变量。同样,如果涉及到的成员变量是高级数据类型,则只需要为被测试函数需要读取的域或成员设定初始值,不必考虑其他无关的数据




这个示例涉及到成员变量的读写




由于VU无法确定程序需要读写哪些成员变量,所以只为参数生成了输入数据,成员变量需要由用户填写,语法是用.操作符表示成员变量




注意,表示成员变量的.操作符必须是第一个字符。只要为产品类添加了测试支持代码,就可以直接访问任何私有或保护的成员变量




这是测试结果

可以看到成员变量的输入值和输出结果




如果成员变量是高级数据类型的对象或对象指针,可以直接调用它的操作,只要符合C++语法就行了




必要时,可以用.操作符直接调用成员函数,只要产品类添加了测试支持代码,测试用例中就可以访问任何成员函数包括私有和保护的成员函数




前置操作,例如先打开一个文件




总之,在测试用例中,可以用.操作符访问任何成员变量,可以用符合C++语法的任何方式为成员变量设定初始值,还可以用.操作符调用任何成员函数


菜鸟
2006-12-28 02:12:00     打赏
12楼
预期输出
完整的测试用例一般应该有预期输出。一个测试用例可以有0项到多项的预期输出,每项预期输出占一行。只要计算结果为布尔值的表达式都是可行的预期输出,与
输入一样,用.操作符访问成员变量和成员函数,可以使用各种C++语法。如果实际输出与预期输出不同,VU会自动报告错误

预期输出可以包括:返回值、输出参数、成员变量、全局变量四种。全局变量很少见,本节主要介绍前三种




返回值是最常见的输出。只要被测试函数的返回值不是void,VU都会生成这一行




也就是说,不管返回值是什么数据类型、也不管返回值是对象、引用、还是指针,一概用ret表示。这个"=="只表示这里可能需要判断输出是否符合预期,对于定义了==操作符的数据类型,可以把预期的结果值直接填在==后面,其他情形需要根据返回类型灵活处理



基本数据类型或其他定义了==操作符的数据类型,把预期的结果值直接填在这里




这个函数的返回值是char*类型,没有定义==操作符。函数的功能是去除字符串两边的空格,返回值指向去除了两边空格的字符串



这里不能把结果直接填在==后面,而是要使用比较字符串的函数



使用strcmp比较字符串



很常见的一种情形是返回对象指针或引用,可以用各种符合C++语法规范的方式判断输出是否符合预期,例如:



返回值是对象指针



第二种常见的预期输出是输出参数(引用或指针),如果被测试函数修改了某个输出参数,一般也要判断它是否符合预期。当然,对于高级数据类型,这里也只需判断被修改了的域或成员



同样,可以使用各种符合C++规范的语法,只要表达式的最终运算结果是布尔值就行



象这种表达式都是可行的



第三种常见的预期输出是成员变量,也就是对象的后置状态。与输入一样,用.操作符直接访问成员变量



定义了==操作符的成员变量,直接调用==操作符



符合C++语法规范的布尔表达式都是可以的



预期输出还可以是不等式,甚至是复合不等式



这些都是合法的预期输出



有时候,预期输出难于计算或录入很麻烦,可以事后确认。这个程序是把人民币小写转换为大写。value是输出参数(引用),保存计算结果:大写字符串



要把结果写出来并不容易,那么可以不写,或者随便填写一个值



运行测试后,看一下结果对不对



双击可以让子窗口最大化



对的话,可以确认



右击打开快捷菜单



看一下测试用例编辑器,预期输出已经自动填好了。事后确认并不是多此一举,因为代码包括调用的底层代码是随时可能被修改的,必要的预期输出可以自动检查修改是否引入了错误



确认预期输出时,VU会使用==操作符生成预期输出表达式,因此,不能用于未定义==操作符的数据类型如char*,否则会产生编译错误



最后总结一下:

一个测试用例可以有0项到多项的预期输出,每项占用一行;

任何类型的返回值都用ret表示;

与输入一样,用.操作符访问成员变量和成员函数;

可以使用各种C++语法,只要计算结果为布尔值就行;

难于计算或录入的预期输出可以运行测试后确认。


菜鸟
2006-12-28 02:12:00     打赏
13楼
代码模式
代码模式下可以直接编辑测试用例代码,用于处理一些特殊情形,比较常见的有:


添加/查看用例注释;


使用缺省值参数;


输入空指针参数;


特殊的测试要求

点击这里切换到代码模式,可以看到当前测试用例的完整代码



加亮的两行代码的功能是生成被测试对象和销毁被测试对象



中间这行代码执行被测试函数



输入数据



断言预期输出



添加/查看用例注释

///后面是为添加注释预留的位置。

编辑和阅读测试用例注释均需在代码模式下进行



使用缺省值参数

在这个示例中,参数int years可以使用缺省值,现在想添加一个测试用例使用缺省值进行测试



把使用缺省值的参数删除就行了



回到数据模式,可以看到,这里也将参数删除了



输入空指针参数

要输入空指针也要在代码模式下进行。

现在想增加一个测试用例,参数CMyClass* pObj为空指针



修改变量声明,由对象改为指针,并赋NULL值,同时删除调用代码中的取地址操作符



特殊的测试要求

例如,现在想连续执行被测试函数两次



通常不需要编辑源代码文件,但在产生编译错误时,由于定位到出错位置很方便,所以在源代码文件中修改会更方便些



假如这里把数字写成了字符串



编译时就会产生错误,并且很容易定位到出错位置,可以直接修改



在源代码文件中修改与在测试用例编辑器中修改,结果是一样的


菜鸟
2006-12-28 02:12:00     打赏
14楼

异常测试
有些程序,在某种输入时,应该抛出某种异常,有两种情形:


一是抛出某个异常类;


二是抛出某个值,如一个整数或一个字符串

这个函数中,当j==0时,应该抛出CMyException类异常,当j==1时,应该抛出int类型异常,其值为-1。

现在演示如何对抛出异常进行测试




这是普通的测试用例



现在新建一个测试用例,用来测试应该抛出CMyException的情形



抛出异常的测试用例与其他用例不同的是,要在输入数据的最后添加:

TEST_EXCEPTION()

并在预期输出的最前添加:

TEST_THROW(异常类)



j == 0
才应该抛出异常,我们暂时不改动j,那么这个用例应该产生一个失败的断言



运行测试,可以看到一个失败的断言,表示预期输出是抛出CMyException类型的异常,实际上并没有抛出



改一下输入再试



测试通过,表示程序抛出了预期的异常



现在演示抛出某个值的情形



当j==1时,应该抛出-1。这里,

TEST_THROW(异常类) 要改为:

TEST_THROW_VALUE(异常类, 异常值)



执行测试,测试通过


菜鸟
2006-12-28 02:13:00     打赏
15楼
5.白盒测试


本节开始讲述白盒测试。

白盒测试就是依据代码及其逻辑结构来设计测试用例进行测试。VU并不把白盒测试作为独立的测试方法,而是在基本功能测试的基础上,统计白盒覆盖状况,并为未覆盖的逻辑单位设计测试用例以达到完整的白盒覆盖,这样,既保证了测试的完整性,又避免了重复工作




VU可以达到100%语句、条件、分支、路径覆盖。语句和条件的覆盖状况由代码窗口显示,分支和路径的覆盖状况由逻辑结构图显示。本节讲述语句和条件覆盖,我们把语句覆盖和条件覆盖合称为代码覆盖。VU不但显示代码覆盖状况,还显示某一用例所执行的代码



随便举一个例子

随便填写一个测试用例

运行测试后,可以清楚地看到覆盖了哪些代码,及当前用例执行了哪些代码



这里把代码分为两种:条件和语句块,加亮的都是条件



VU统计条件覆盖时,只考虑条件是否被计算,被计算了的条件视为已覆盖,不考虑计算结果



加亮的代码都是语句块,一个语句块是指一组没有分支的语句,如果其中有一条语句覆盖了,除非产生了异常,否则其他语句也会被覆盖



黑色代码是当前用例执行了的代码,红色代码是当前用例未执行的代码,红色并加了淡蓝色背景的代码是未覆盖代码,即所有用例均未执行的代码



这里,我们可以清楚地看到当前用例执行了哪些代码。

首先是执行这一行

计算这个条件,为真

执行这一行

执行这一行




查看当前用例所执行的代码的另一种简便方式是把鼠标移到路径入口



随便加一个测试用例再看看



运行测试后,可以看到,所有条件都覆盖了(即都计算过了),还有三行代码未覆盖。黑色的代码是当前用例已执行的代码



从这里看看当前用例所执行的代码



可以通过切换当前用例来查看各个用例所执行的代码。右击打开快捷菜单



也可以只显示代码的覆盖状况


菜鸟
2006-12-28 02:13:00     打赏
16楼
逻辑结构图基础

路径窗口自动画出程序逻辑结构图。这是新一代的逻辑结构图,提供了丰富的信息和操作





这种矩形表示一个语句块,对应于左边代码窗口加亮的代码





鼠标放在上面稍作停留,会显示对应的代码





点击语句块可以选中它





这是一个分支结构,对应于代码窗口中加亮的代码





这种黑色矩形是分支结构的操作矩形,点击它可以选中该分支结构,鼠标放在上面稍作停留会显示对应的代码





点击"操作矩形",可以选中该分支结构





这个分支结构包含两条分支,其中第二条分支是空分支





这是分支标注,显示分支的条件。鼠标放在分支标注上稍作停留,会显示整个分支对应的代码





点击分支标注,可以选中该分支





由入口到出口的粗线条表示程序的一条路径,用绿色画出的路径是已覆盖路径,用橙色画出的路径是未覆盖路径





程序入口,也就是所有路径的入口





程序自然出口





return 语句形成的出口,当程序含有return语句时,可能会形成多个出口





把鼠标放在路径入口上,会显示该路径的代码





右击可以打开快捷菜单





点击这里切换路径





在执行这些命令时,快捷菜单会自动关闭,把菜单"钉住"则不会在执行命令时自动关闭,但右击仍会关闭





可以快捷地浏览各条路径的代码





红色的判定表示计算结果为假。在这条路径中,连续两个判定的计算结果均为假,所以只执行了最后一条语句





绿色的判定表示计算结果为真,在这条路径中,连续两个判定均为真





逻辑结构图中,把循环结构视为至少执行循环体一次和从不执行循环体两个分支,即进入循环和不进入循环两个分支,不考虑循环的次数





循环体内又可能有分支结构,有continue、break语句产生的跳转,这些代码逻辑比一般的分支结构更复杂。当程序含有复杂循环结构时,一个测试用例可以覆盖多条路径





这里只有一个测试用例,却覆盖了五条路径





来看一下这个while循环的代码

菜鸟
2006-12-28 02:13:00     打赏
17楼
逻辑结构图裁剪

如果代码逻辑很复杂,逻辑结构图也会很复杂,可能会有几十个分支,几百上千条路径。逻辑结构图裁剪功能,使这种复杂代码也能轻松达到完整的白盒覆盖

逻辑结构图裁剪包括三方面:

一是删除不可覆盖的分支,一个分支删除后,所有通过该分支的路径都会自动删除,从而大幅度减少路径数量;二是删除不可覆盖的路径;

三是屏蔽或临时屏蔽部分次要的逻辑结构,从而简化逻辑结构图,使注意力集中在主要的部分




逻辑结构图中,语句块、分支、分支结构、路径均是可以删除/恢复的,选中一个对象后,使用快捷菜单即可删除/恢复,系统会重新统计分支和路径



录制本教程时使用800*600的屏幕分辩率,复杂的图形不能完整显示。如果逻辑结构图不能完整显示,可以调整显示比例



删除/恢复语句块



删除/恢复分支



已删除的分支会在入口处画个红X



删除/恢复一个分支结构



已覆盖的路径是不能删除的,只能删除未覆盖的路径。绿色画出的是已覆盖路径



点击路径入口选中当前路径



橙色画出的是未覆盖路径,只有未覆盖路径才可以删除。删除未覆盖路径一般用于标记不可覆盖的路径



如果一个分支不可覆盖,那么所有通过该分支的路径都不可覆盖。删除一个分支后,所有通过该分支的路径都会自动删除



现在有129条路径



看一下删除这个分支后还有多少条路径



剩下65条,几乎减了一半



再删除一个分支,只剩下33条



现在看一下删除路径。有很多路径都是通过浏览它的代码即可认定是不可覆盖的,可以直接删除



删除路径后,信息窗口会视为该路径不存在,即总的路径数量会减少



但在逻辑结构图中,只是在路径的入口处做个标记



这里显示的路径数量不会减少,被删除的路径在逻辑结构图中仍然保留,可以随时恢复



下一条



除了删除不可覆盖的分支、路径外,另一种常用的功能是屏蔽部分对象,主要是屏蔽分支结构






有些代码形成了分支结构,但这些分支结构实质上对代码的逻辑是没影响的,例如处理缺省值参数的代码:if(len
== -1) len = strlen(str);这行代码可能使路径数量翻番,但它对程序的整体逻辑是没有影响的。当逻辑结构图很复杂时,可以将这种代码形成的分支结构屏蔽



从图上看,与一个复杂的分支结构并列的一个或多个简单的分支结构,通常用于处理一些简单逻辑,这些程序逻辑往往与主要的程序逻辑无关,在逻辑结构图很复杂时可以首先考虑屏蔽



屏蔽任何分支结构时,都要先看一下它对应的代码,了解它的功能,以确认是否可以屏蔽



四个分支结构都是用于前期计算,与后面的复杂逻辑没有关系,因此可以屏蔽它们,即在图上删除它们



这是屏蔽后的结果



菜鸟
2006-12-28 02:14:00     打赏
18楼
异常处理逻辑

try/catch
结构形成了特殊的分支结构,也形成的特殊的程序路径。VU对复杂的异常处理逻辑,也能精确地计算路径和统计路径覆盖





从代码格式上看,try/catch好象构成了一个分支结构





实际上,try块只是一个范围,它不是一个分支





catch语句,一个或连续的多个catch,形成一个完整的分支结构





与普通分支结构不同的是,只有产生了异常的路径才会进入catch语句形成的分支结构,其他路径会直接跳过





try块内的语句块,都会带有"e"标记,表示肯定抛出异常或可能产生异常





不透明的"e"表示肯定抛出异常,即含有throw语句





半透明的"e"表示可能产生异常,即存在产生异常和不产生异常两种可能,实际上形成了一个特殊分支,会造成路径数量的增加





从代码可以看出,这里是肯定不会产生异常的,因此要"关闭"产生异常的可能性,以免造成不必要的路径数量增加





现在有26条路径





"关闭"后,"e"标记消失





路径数量减为21条





逻辑结构图不能表达从分支条件中产生异常的情形,因此,从条件中产生的异常在逻辑结构图中视为不存在,这种情况下可能会造成某些用例的执行路径无法在逻辑结构图中画出来,最好不要将可能产生异常的语句放在分支条件中





路径中的"e>>"表示这里抛出了异常





此后,该路径将跳过后面的代码(用虚线画出),直到进入catch结构





catch结构内又可以再次抛出异常





再次抛出异常后,又跳过后面的语句,只能由上级函数来处理了





在这条路径中,进入catch结构后,没有再次抛出异常,后面的代码又可以正常执行了





这个语句块可能产生异常,这条路径是产生了异常的情形





这条路径是没有产生异常的情形,路径将跳过catch结构

菜鸟
2006-12-28 02:14:00     打赏
19楼
用例设计器基础
只要有一个可以完整运行的测试用例,就可以统计白盒覆盖状况,并使用测试用例设计器来设计其他测试用例。不过,测试用例设计器主要用于找出遗漏的测试用例,对于容易想到的测试用例,由测试用例编辑器来建立会更高效些





还是用这个示例来讲述测试用例设计器的基本知识,下一节再用一个复杂些的例子来作进一步阐述





随便输入第一个测试用例





运行测试后,可以清楚地看到白盒覆盖状况





未覆盖语句





未覆盖条件





未覆盖分支


分支标注的背景色为淡蓝色的分支是未覆盖的分支





覆盖的顺序应该是:


语句覆盖、条件覆盖、分支覆盖、路径覆盖


这样由易到难,工作量最小





单击选中一个未覆盖语句块





右击打开快捷菜单





打开测试用例设计器





下半部分与测试用例编辑器很相似,这里显示的是一个近似测试用例。近似测试用例是从现有的测试用例中选择的、经过最小修改即可覆盖选中逻辑目标的测试用例





上半部分显示修改提示,提示修改哪个数据和如何修改,根据修改提示,对近似测试用例进行修改后,即可得到预期的测试用例





这里提示的是 A>1 &&
B==0,在近似测试用例中,B本来就等于0,所以只要把A改为大于1的数就可以了。这种蓝色的粗体字显示的是待满足条件,也就是新的测试用例必需满足的条件,后面还会出现已满足条件





修改了输入数据,预期输出也要修改(也可以事后确认),为了简化说明,这里不考虑预期输出





点击这里完成新的测试用例的建立。由于一个测试用例可能覆盖多个逻辑单位,所以每建立一个测试用例,就应该运行测试,然后覆盖剩余的逻辑单位





运行测试后,可以看到这个语句块已被覆盖了





这里多了一行黑色的提示,叫依赖关系提示,表示待满足条件所涉及的变量可能被其他代码修改。如果要修改的变量名出现在依赖关系提示中,要点击"显示代码"按钮查看代码





现在来看一下待满足条件,两个条件是逻辑或的关系,所以任选一个条件都可以。后面再讲解选择第二个条件的情形,这里选择第一个条件:A==2,即把改为2





运行测试后,可以看到所有语句和条件都被覆盖了





语句、条件、分支都已完成100%覆盖,只有一条路径未覆盖





这种蓝色的非粗体显示的条件叫已满足条件,是近似测试用例已经满足的条件,建立新的测试用例时不能破坏这些条件。如果待满足条件与已满足条件冲突,表示逻辑单位不可覆盖





还是两个逻辑或关系的条件,但变量A在已满足条件中出现过,选择X>1比较简单,即把X改为2就行了。如果使用条件A==2,则在把A改为2的同时要把B改为不等于0的数,这样才能不破坏已满足条件,即把A改2后,A<=1
|| B!=0 仍然成立





运行测试后,可以看到,已经完成了全部的白盒覆盖





现在把后面的两个测试用例删除,讲解一下建立第三个测试用例时,选择第二个条件的情形





重新运行测试后,现在再次设计第三个测试用例





现在选择条件X>1,依赖关系提示中出现了变量X,因此,要查看一下相关代码





点击这里查看相关代码。即使没有出现依赖关系提示,如果条件不能一下子理解,就应该查看相关代码





橙色的条件叫不确定条件,通常可以忽略





橙色的"{"和"}"内的代码,即不确定条件后的代码,表示可能执行也可能不执行





在近似测试用例中,A的值为3,B的值为0,所以,如果不改变A和B的值,那么X
=
X/A是肯定会执行的,所以,在输入参数中,要把X设为大于等于6的数,才能满足X>1这个条件。





运行测试后,可以看到,这行语句已覆盖





覆盖最后一条路径





这里就选个简单的吧,把X改为2就行了





运行测试后,也实现了完整的白盒覆盖


菜鸟
2006-12-28 02:14:00     打赏
20楼
用例设计器进阶
上一节用比较简单的代码讲述测试用例设计器的基本使用方法。这一节用逻辑比较复杂的代码,演示使用测试用例设计器找出隐藏很深的测试用例,及识别不可覆盖的逻辑单位





这个函数的功能是删除字符串两边的空格





已经建立了如下测试用例,可以说已经相当完整了:


char str[] = " abcde "; //两边有空格


char str[] = "abcde "; //右边有空格


char str[] = " abcde"; //左边有空格


char str[] = "abcde"; //两边无空格


char str[] = ""; //空串


char* str = NULL; //空指针





运行测试后,发现了一些错误,不过这一节讲述的是设计测试用例,不处理发现的代码错误





语句和条件已全部覆盖了,有一个分支及四条路径未覆盖





选中这个未覆盖分支,打开测试用例设计器





条件不易理解,需要浏览代码





已满足条件:str != NULL 及 len>0


即输入不是空指针,也不是空串

从代码的整体逻辑来看,一个不是空串的字符串(已满足条件),在删除右边空格之后,要变成空串(待满足条件),那么这有没有可能呢?当然这是可能的,只要输入是一个全是空格的字符串就行了,这样我们就知道如何修改近似用例了



待满足条件是:不进入for循环,也就是说,循环条件for(int
i=0; i<len; i++)中,len的值小于等于0,根据前面的两行代码,可以知道len是字符串的长度,也就是说,程序运行到这里,str必须是空字符串




删掉非空格字符



运行测试后,果然覆盖了这个分支



并且,也发现了一个新的错误



分支覆盖也完成了,还有三条路径未覆盖



待满足条件仍然是不进入循环,即代码运行到这里时,str应该是空串



已满足条件:非空串,非空指针,这里跟设计上一个测试用例时一样



不同的是,增加了两个已满足条件



删除右边空格时进入循环,即右边一定有空格



计算左边空格时也进入循环,即在删除右边空格后,左边仍然有空格,因此,输入肯定含有非空格字符



所以,在不破坏已满足条件的前提下,代码运行到这里时,str不可能是空串,待满足条件与已满足条件冲突,这条路径是不可覆盖的



把不可覆盖的路径删除



来看下一条



其他条件与前面一样,不同之处仅在于,这里不进入循环,即右边没有空格



这里的意义是左边有空格



右边没空格,而左边有空格,所以肯定含有非空格字符,运行到这里时不可能是空串,这条路径也是不可覆盖的



再下一条



待满足条件仍然是一样的



已满足条件的意义是:

非空串;

右边没有空格;

左边也没有空格。

因此,str是左右没有空格的非空字符串,代码运行到待满足条件时,str不可能是空串,这条路径也不能覆盖




完成了全部的白盒覆盖




一般来说,完成语句、条件、分支覆盖后,未覆盖的路径中,大多数都是不能覆盖的,可以通过浏览路径代码的方式快速识别



鼠标移到这里,显示路径代码



把代码提示窗口和菜单窗口都"钉住"



这个标记表示这里是重点,相当于用例设计器中的待满足条件



绿色的是真判定,红色的是假判定,意义也是很容易理解的,根据这些判定和代码,很多不能覆盖的路径都可以识别出来



确认不能覆盖的路径可以直接删除,认为可以覆盖或不能确定的再使用测试用例设计器



这里切换路径,代码会自动刷新


共33条 2/4 1 2 3 4 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]