变异测试 是一种基于错误注入的测试方式,具体来讲就是人为在代码中注入错误,然后来观察现有的测试用例是否能够发现这些错误,如果能够发现说明测试用例是有效的,如果不能发现说明测试用例需要进一步完善和补充。
变异测试起源
变异测试在1970年被一个学生DickLipton提出,首次发现和公之于众,之后在1980年就已经出现了第一个变异测试的工具。
在学术界变异测试的研究已经持续了很长时间,研究的焦点主要集中在变异算法优化以及等价变异体分析上。
但是在工业界对变异测试的关注度一直很低,甚至很多测试技术人员压根不知道什么是变异测试,这背后的原因主要是因为 变异测试需要在单元测试已经做得比较完备的基础上才有其价值。
实施变异测试的步骤
首选我们有被测源代码(P),以及对应的测试用例代码,随后在被测源代码P上,用变异算子(S)生成变异体源代码(P'),这个过程称为变异体生成。用人话来讲其实就是对被测源代码进行“合乎语法的微小改动”,这种微小改动就是所谓的变异算子,比如原本是“加法”运算现在改成“乘法”运算,或者原本是“逻辑与”运算现在改成“逻辑或”运算,之后分别使用相同的测试用例T对被测源代码P和变异体源代码P'执行测试,最后比较测试执行结果。
- 如果两次测试结果都是通过的,说明变异注入的错误并不能被测试用例T感知,这种情况称为变异体能够“存活”,说明测试用例T的有效性存在问题,需要对测试用例进行补充和修正。
- 如果两次测试结果不同,暨在被测源代码P上执行测试通过,在变异体源代码P'上执行测试不通过,说明变异注入的错误能够被测试用例T感知到,测试用例T能够“杀死”此次变异,测试用例的有效性在此类变异上没有问题。
变异测试的几个概念
1、首先我们来了解下等价变体的概念:
源代码如下:
for(int i=0;i<10; i++){ // 源程序 //To-do ... }
变体1如下:
for(int i=0;i!=10; i++){ //变体1 //To-do ... }
变体2如下:
for(int i=0;i<10; i--){ //变体2 //To-do ... }
由此可见变体1与源代码是等价的:i从0开始,经历2,3,4,5,6,7,8,9到10,在源代码中由于10<10返回False,退出循环;在变体1中由于10!=10返回False,退出循环。而变体2与源代码是非等价的:i从0开始,经历-1,-2,-3…永远达不到i<10为False的情形。
2、变异算子
对被测源代码进行“合乎语法的微小改动”,这种微小改动就是所谓的变异算子。Mutpy 中定义了以下27个变异体。
- AOD - arithmetic operatordeletion(删除算术运算符)
- AOR - arithmetic operatorreplacement(替换算术运算符)
- ASR - assignment operatorreplacement(替换赋值运算符)
- BCR - break continuereplacement(交换break和continue语句)
- COD - conditional operatordeletion(删除条件运算符)
- COI - conditional operatorinsertion(插入条件运算符)
- CRP - constant replacement(替换常量)
- DDL - decorator deletion(替换修饰符)
- EHD - exception handlerdeletion(删除异常处理)
- EXS - exception swallowing(吞咽异常)
- IHD - hiding variable deletion(删除隐藏变量)
- IOD - overriding methoddeletion(删除覆盖方法)
- IOP - overridden method callingposition change(重写调用位置更改的方法)
- LCR - logical connectorreplacement(更换逻辑连接器)
- LOD - logical operator deletion(删除逻辑运算符)
- LOR - logical operatorreplacement(替换逻辑运算符)
- ROR - relational operatorreplacement(替换关系运算符)
- SCD - super calling deletion(删除超级调用)
- SCI - super calling insert(插入超级调用)
- SIR - slice index remove(移除切片索引)
- CDI – class method decoratorinsertion(插入类方法装饰器)
- OIL - one iteration loop(一个迭代循环)
- RIL - reverse iteration loop(反向迭代循环)
- SDI – static method decoratorinsertion(插入静态方法装饰器)
- SDL - statement deletion(删除语句)
- SVD - self variable deletion(删除自变量)
- ZIL - zero iteration loop(零迭代循环)
3、一阶变异体 和 高阶变异体
看下面代码
[A] z = x * y [B] z = x / y [C] z = x/y*2 [D] z =4x/y*2
B是A的一阶变异,C是B的一阶变异,D是A的高阶变异。
测试方法
如果这个过程中,有减分,说明测试用例不完善或者出现重复的测试用例。
测试工具
在变异测试中Java常用的工具为 PITest,Python常用的工具为 Mutpy。