变异体杀手的诞生之路
2026/9/23 11:24:28 网站建设 项目流程

先来回顾一下变异测试的理念和方法。
评估质量和检出缺陷,是测试的两个基本目的。那么,什么样的测试集是充分的?
设被测对象为P,被测对象期望为F,测试输入空间为D。在评估质量的意义上,如果测试集T满足∀t∈T,P(t)=F(t)⇒∀t∈D,P(t)=F(t),则T是充分的;在检出缺陷的意义上,如果T满足∃d∈D,P(d)≠F(d)⇒∃t∈T,P(t)≠F(t),则T是充分的。
也就是说,所谓充分的测试集,应该能够检出所有可能的缺陷。要达到这样的充分性要求,难度不言而喻。然而如果我们对被测对象的质量有相当的信心,也可以适当降低测试充分性标准,比如只要求测试集对在一组特定的缺陷敏感。这就是变异测试的出发点。
在原始被测对象中模拟植入某个特定缺陷之后,我们就得到了一个变异体。如果测试集中的某个用例在原始被测对象上的执行结果,与其在变异体上的执行结果不同,变异体就被测试集杀死了。否则,我们可能就需要补充新的用例,以强化测试集的缺陷检出能力。关键是,补充什么样的用例,才能成功杀死变异体呢?
首先,这个用例在执行的时候,必须要能够触达发生变异的位置,这是我们发现变异的基本前提。但仅仅如此还不够,用例还需要提供足够的刺激,诱发变异体产生一些不正常的行为。最后,这些不正常的行为还要在被测对象外部表现出来,这样我们才能观察到。
所以简单来说,要想成为一个优秀的变异体杀手,测试用例需要具备三项能力:
1、执行能力:执行用例时能够覆盖发生变异的结构元素;
2、感染能力:用例能够引发被测对象内部状态的异化;
3、传播能力:上述异化能够传导至输出并被观察到。
来看一个简单的例子。假设被测程序如下:

void example(int a, int b, int c){ int m = 0; int result = 0; if (a >= b){ m = a + b; } if (m >= c){ result = c; } System.out.println("结果: " + result); }

在程序第5行引入变量替换变异,将变量a替换为常量3,以此来模拟一个未来可能发生的编码错误。这时可以得到如下变异体:

void example(int a, int b, int c){ int m = 0; int result = 0; if (a >= b){ m = 3 + b; } if (m >= c){ result = c; } System.out.println("结果: " + result); }

我们来看看,要杀死这个变异体,需要一个什么样的用例:
1、首先,为了能够执行到发生变异的第5行代码,用例需要满足a≥b;
2、其次,软件的内部状态通常体现为各个变量的取值。上述变异的发生,有可能对内部变量m产生影响:只有当a≠3时,原始被测程序和变异体中m的取值才会出现差异,变异体才会表现出感染症状;
3、最后,如何让m的感染症状传播至程序输出结果呢?通过对被测对象的数据流分析,可知m与输出变量result之间不存在定义使用关系,m对输出的影响仅体现在控制流方面。只有让决策逻辑走上不同的分支,即满足a+b≥c ≠ 3+b≥c,我们才有可能在输出中发现异样。此外还必须排除c=0的情况,因为不同决策分支在这一情况下会产生相同的结果。
将上述所有约束条件束联立起来:

找到满足所有这些条件的用例,一个合格的变异体杀手就诞生了,比如a=1, b=1, c=3。
容易看到,想要完整列出变异体杀手的合格条件(特别是在传播能力方面),难度是比较高的,而且对约束条件的联立求解也并非易事,这就是主流变异测试工具都不具备用例生成能力的原因。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询