背景简介
在做单元测试时,一般都要求对被测程序的结构特性做到一定程度的覆盖,并以软件中的某类成分是否都已经得到测试为准则来判断软件测试的充分性,也称为基于覆盖的测试技术。例如,语句覆盖是一种逻辑覆盖准则,它要求选择测试数据使得程序中所有语句都得到运行,并根据是否所有语句都得到了运行来决定测试是否可以终止。到目前为止,已提出了几十种覆盖技术。
在这些覆盖的技术中,覆盖率最高的就是路径覆盖技术。路径覆盖,要求程序的每条可能路径都至少执行一次,如果程序中有环,则要求每个环至少经过一次。一般来说,语句覆盖是很弱的逻辑覆盖标准,判断覆盖比语句覆盖强,条件覆盖通常比判定覆盖强,判定一条件覆盖是判定覆盖和条件覆盖的综合,条件组合覆盖则比前面几种覆盖标准要强,但并不一定比路径覆盖强。
下面我们来讨论完全路径覆盖测试方法和基于搜索的路径寻找方法的研究。
基于朴素的深度优先搜索算法搜索
我们不难发现,程序的流程图也是一种图,所以肯定可以通过搜索的方法来寻找其中的路径。虽然对于简单的流程图我们直接可以找出全路径,但是我们实际项目过程中,往往有很多逻辑比较复杂,分支几十个的模块,这些地方人眼肯定不能直接找出所有路径,就是慢慢寻找也会很容易遗漏,不过要是能用计算机来做这件事无论是效率还是覆盖率上肯定都会好很多。那具体要怎么做呢?
模型建立
对于一个程序片段,我们无法直接去分析,使用先要将其抽象成可通过计算机处理的模型。其主要步骤有下面几点:
-
分析函数逻辑,画出程序流程图;
-
对于需要建模的部分进行编号,但要注意并不是所有的模块都需要,我们只要对有分支的部分进行编号即可;
-
将各个模块(已编号的)之间的连接关系表示出来。
深度优先搜索算法简介
-
从图中某顶点v出发,先访问顶点v;
-
依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;
-
若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。
全路径寻找
对于上面已经建好的图,采用搜索的方法找出全部的路径。这里建议用计算机来搜,可以写个深度优先搜索的小程序,即省时省力有方便可靠。
(戳图,可放大看)
对于搜索出来的所有路径进行case设计
计算机搜出来的是死的路径,而我们需要的是能灵活覆盖各种情况的case。比如某个if里有多条判断语句时,我们在设计包含该点的路径时就要考虑多种情况,还有各种边界值我们也需要去检查等等。总之这种方法只是为我们测试人员提供一点点便利,具体的case设计还要我们自己去仔细斟酌,考虑每个可能会有问题的地方。
使用有深度限制的迭代加深搜索算法
上面的方法虽然能解决一部分问题,但是我们不难发现,这种朴素的深度优先搜索一旦遇到有环图就会陷入死循环,而我们日常项目中这种情况又是非常常见,一旦有一个while循环就会是一个环。然后这里就想到了使用更优的迭代加深搜索。
迭代加深搜索简介
迭代加深搜索,实质上就是限定下界的深度优先搜索。即首先允许深度优先搜索K层搜索树,若没有发现可行解,再将K+1后重复以上步骤搜索,直到搜索到可行解。
具体实现
这里建模过程和上面一样,只是搜索的算法有所改变,在搜索时有深度限制,这样就不会出先死循环。
(戳图,可放大看)
使用启发式搜索的人工智能搜索算法
尽管上面已经可以解决有环图的死循环问题,但从case我们不难发现有好多冗余的case,这也是使用迭代加深不可避免的,对于有环图,只是限制搜索深度,毕然会有一些路径被重复搜索。那怎么办?于是乎想到了使用人工智能中的一些算法来控制搜索方向,下面就简单介绍下:
A星搜索
A星搜寻算法,俗称A星算法,作为启发式搜索算法中的一种,这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法。
A*算法最为核心就在于它的一个估值函数的设计上:f(n)=g(n)+h(n)
, 其中f(n)是每个可能试探点的估值,它有两部分组成:
g(n)
,它表示从起始搜索点到当前点的代价(通常用某结点在搜索树中的深度来表示)。
h(n)
,它表示启发式搜索中最为重要的一部分,即当前结点到目标结点的估值,h(n)
设计的好坏,直接影响着具有此种启发式函数的启发式算法的是否能称为A*算法
。
爬山算法
爬山算法
是一种局部择优的方法,采用启发式方法,是对深度优先搜索的一种改进,它利用反馈信息帮助生成解的决策,属于人工智能算法的一种。
算法解释:
从当前的节点开始,和周围的邻居节点的值进行比较
如果当前节点是最大的,那么返回当前节点,作为最大值(既山峰最高点)
反之就用最高的邻居节点来,替换当前节点,从而实现向山峰的高处攀爬的目的
如此循环直到达到最高点
我们做法
不难看出,我们可以可以利用A星搜索的思想来控制我们的搜索深度,同时利用爬山算法的思想来指导我们搜索的方向,这样既能保证搜索结果的数量足够小又能保证搜索的质量。
我们的具体做法如下:
-
用
v(n)
表示n节点已经被访问过的次数 -
用
h(n)
表示n节点还可以访问的次数 -
先根据
v(n)
从小到大 -
再根据
h(n)
从大到小的顺序 -
依次访问节点
其中h(n)
是类似于启发式函数,这个的设计尤其关键,对于不同类型的模块可能需要不同的启发式函数,我这里目前采用的是通过再一次的的搜索来预估当前节点还需被访问的次数。
总结
以上这些是在工作之余想出来的一些小技巧,希望对大家能有帮助,同时大家能有什么好的建议也希望能及时给我提。
如需转载该篇文章,请注明来自“搜狗测试”