单元测试用例设计.docx
《单元测试用例设计.docx》由会员分享,可在线阅读,更多相关《单元测试用例设计.docx(8页珍藏版)》请在冰豆网上搜索。
单元测试用例设计
一、概述
单元测试(模块测试)是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。
通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。
该文档从测试角度出发,去讨论如何设计单元测试的测试用例。
这里强调,单元测试用例的设计是进入实际编码之前的,测试用例设计在前,更能体现出灵活性,如果已经编码完成再进行测试用例的补充,这样很容易进入一个仅仅是测试了被测代码段功能的怪圈,所以希望所有的单元测试工作,可以放在前面完成。
同时单元测试用例是一个不断完善的过程,前期设计好的用例,在代码已经实现完成后,会发现覆盖的并不是很全面,有良好的习惯是需要将对应的测试用例进行补充,而在提交测试后发现的重要的bug,也需要进行单元测试用例的补充,使单元测试和各种测试方法相结合,实现测试质量的充分保证。
二、基本概念
正面测试(PositiveTesting)
测试被测对象的正确功能实现无误,即正常流程功能。
往往需要根据设计说明进行用例导出,严格按照设计说明编写即可,用例划分注意等价类区分等方法。
例如:
接口返回小于等于24个中文字的offer标题(这里标题控制不会超过24个字)进行页面展示。
负面测试(NegativeTesting)
测试被测对象的异常功能实现无误,多在异常流程,异常数据中体现。
该部分测试需要对被测对象进行错误发散,常依赖于边界值区分等方法。
例如:
接口返回25个中文字的offer标题进行页面展示。
分支测试
使用流程图,明确可能出现的每条分支,制造响应的数据进行覆盖,实现对被测对象的测试。
这个过程对于分支可以进行响应的简化,可以穿插等价类等方法去除同类分支。
例如:
实现offer发布的功能,分别会出现发布普通产品,代理加盟,求购,供应等分支,测试offer提交模块的时候,需要区分这么多重类型的数据,那么假设对于全部供应类型的offer,实现上都是一样的,就可以进行等价类划分,区分供应和求购即可。
黑盒测试
不关心被测对象内部,将其当做一个黑盒,仅仅关注对该模块的输入区分和输出结果校验。
白盒测试
将被测对象的每个实现都充分了解,根据内部实现进行用例设计,需要保证每个独立路径都完成用例覆盖,而常规的对每个独立路径进行真假验证。
三、单元测试范围
单元测试范围的重点包括两个方面:
1.测试代码实现的功能,这个可以通过需求文档进行整理,然后调整每个功能点的颗粒度,尽量可以和开发实现的被测单元进行对应,入口文档包括需求文档、设计文档;2.外部接口和底层实现。
4、常见测试用例设计方法及举例
用于语句覆盖的基路径法
基路径法保证设计出的测试用例,使程序的每一个可执行语句至少执行一次,即实现语句覆盖。
基路径法是理论与应用脱节的典型,基本上没有应用价值,读者稍作了解即可,不必理解和掌握。
基路径法步骤如下:
1)画出程序的控制流图
控制流图是描述程序控制流的一种图示方法,主要由结点和边构成,边代表控制流的方向,节点代表控制流的汇聚处,边和结点圈定的空间叫做区域,下面是控制流图的基本元素:
以下代码:
voidSort(intiRecordNum,intiType)
{
intx=0;
inty=0;
while(iRecordNum-->0)
{
if(0==iType)
{
x=y+2;
break;
}
elseif(1==iType)
{
x=y+10;
}
else
{
x=y+20;
}
}
}
可以画出以下控制流图:
2)计算程序环路复杂度
环路复杂度V(G)可用以下3种方法求得:
(1)环路复杂度等于控制流图中的区域数;
上图中,有4个区域,V(G)=4。
(2)设E为控制流图的边数,N为结点数,则环路复杂度为E-N+2;
上图中,V(G)=10(边)–8(结点)+2=4。
(3)设P为控制流图中的判定结点数,环路复杂度为P+1。
上图中:
V(G)=3(判定结点)+1=4。
环路复杂度是独立路径数的上界,也就是需要的测试用例数的上界。
3)导出基本路径集
基本路径数等于V(G)。
根据上面的计算方法,可得出需要的基本路径数为4。
路径就是从程序的入口到出口的可能路线,基本路径要求每条路径至少包含一条新的边,直到所有的边都被包含。
需要提醒的是:
基路径法和路径覆盖是两回事,用于设计用例的基路径数一般小于全部路径数,即基本路径集不是惟一的。
基路径法完成的是语句覆盖,而不是路径覆盖。
下面选择四条基本路径:
路径1:
1-11
路径2:
1
路径3:
0-1-11
路径4:
0-1-11
4)设计用例
根据上面的路径,可以设计出以下用例:
路径1:
1-11
用例1:
iRecordNum=0
路径2:
1
用例2:
iRecordNum=1,iType=0
路径3:
0-1-11
用例3:
iRecordNum=1,iType=1
路径4:
0-1-11
用例4:
iRecordNum=1,iType=2
从上述步骤可以看出,基路径法工作量巨大,如果用于五十行左右的函数,将耗费大量的时间,而五十行代码的函数实在是太普通了。
这种成本巨高的方法,其测试效果如何呢测试效果完全与成本不匹配,首先,基路径法完成的只是代码覆盖,这是最低级别的覆盖,其次,整个设计过程都是依据已经存在的代码来进行的,没有考虑程序的设计功能,是典型的“跟着代码走”,不足是显而易见的。
综上所述,基路径法没有实际应用价值。
用于MC/DC的真值表法
设计用于MC/DC的用例,可以先将条件值的所有可能组合列出表格,然后从中选择用例,称为真值表法。
例如判定A||(B&&C),条件组合如下表:
A
B
C
判定结果
组合1
T
T
T
T
组合2
T
T
F
T
组合3
T
F
T
T
组合4
T
F
F
T
组合5
F
T
T
T
组合6
F
T
F
F
组合7
F
F
T
F
组合8
F
F
F
F
为了使A独立影响判定结果,选择B和C相同,判定结果相反,且A相反的组合:
组合2和6;
为了使B独立影响判定结果,选择A和C相同,判定结果相反,且B相反的组合:
组合5和7;
为了使C独立影响判定结果,选择A和B相同,判定结果相反,且C相反的组合:
组合5和6。
因此,组合2、5、6、7符合MC/DC要求。
符合MC/DC要求的用例集不是惟一的。
为了提高效率,可以使用工具来生成真值表和找出符合要求的组合,有些商业工具具有这种功能。
自行开发难度也不大,下面提出开发MC/DC用例设计小工具的思路,有兴趣的读者可以尝试一下:
1)用一个简单的词法和语法分析器解析判定表达式,计算条件数量;
2)生成真值表;
3)用一个逻辑表达式计算器,针对每个条件C,扫描真值表,找出符合以下要求的组合:
除条件C外,其他条件取值相同;将条件C的真值和假值分别代入判定表达式,判定的计算结果相反。
4)针对找出的组合,设计两个用例,条件C分别取真和假。
需要注意的是,判定中可能存在完全相同的条件,例如:
(A==0||B==1)&&C==2||(A==0&&D==3)
针对A==0设计MC/DC用例时,前一个A==0取反,后一个A==0也会跟着取反,如果后一个A==0视为其他条件,则不能实现MC/DC覆盖,因此,计算判定值时,两个A==0应视为同一个条件。
边界值法
边界值法假定错误最有可能出现在区间之间的边界,一般对边界值本身,及边界值的两边都需设计测试用例。
如下函数:
左边有空格:
删除;(正常输入)
2.左边无空格:
不作处理;(正常输入)
3.全部是空格:
全部删除;(正常输入)
4.空串:
不作处理;(边界输入)
5.空指针:
直接返回。
(非法输入)
不一定需要针对每个功能点分别写代码,因为程序中的if、for、while等语句本身具有“如果不符合条件就跳过”的含义,所以很多功能点是可以共用代码的,例如,前4个功能点只需要相同的代码,不过,编程时对功能点的考虑还是要全面。
既然函数没有错误的关键是等价类划分正确完整且处理正确,那么测试时,只要把输入的等价类都列出来,并设定正确的预期输出,进行测试就行了。
这就是通常说的“等价类”法,从测试角度来说的“等价”,是指测试效果上的等价,即同类中一个数据测试通过,可以肯定其他数据也会测试通过。
用例设计的首要工作是设定输入。
输入有哪些呢要从正常输入、边界输入、非法输入三方面考虑,每方面进一步划分形成等价类,即要考虑:
有哪些正常输入有哪些边界输入有哪些非法输入
多个输入时,例如有多个参数,首先把各个参数的等价类列出来,然后要考虑参数之间是否存在特殊的组合关系。
例如下面的函数:
元测试的命名标准
合理地命名测试,主要目的是为了使后来的开发者从为了理解测试而阅读代码的负担中解脱出来。
测试名应该包含三部分:
被测试方法名、测试场景(即测试使用的条件)、预期行为(即被测试方法的最终结果)。
2.单元测试中的变量命名规范
单元测试除了主要的测试功能之外,它还为API提供某种形式的文档。
通过合理命名变量,帮助阅读测试的人可以尽快理解你要验证什么(从而更加理解产品代码中想要实现什么功能)。
3.断言和操作分离
4.避免滥用setup和teardown
比如在setup中准备stub和mock对象,这种情况就会导致阅读测试的人意识不到测试中使用了模拟对象,也不知道这些模拟对象预期是什么。