变异测试在1970年被一个学生DickLipton提出,首次发现和公之于众。变异测试最初是为了定位揭示测试单元的弱点。这个理论是:如果一个边缘被引入,同时出现的行为(通常是输出)不受影响的情况下,那么这说明了:变异代码从没有被执行过(产生了过剩代码)或者测试单元无法定位错误。
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. 6个概念
在变异测试中需要关注以下六点
1)变异算子
1987年,针对Fortran 77语言定义了22个变异算子,而在下面我们介绍的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(零迭代循环)
2)一阶变异体
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的高阶变异
4)可删除变异体
如果测试用例测试源代码和测试编译代码不一致,则这个测试用例可以删除
5)可存活变异体
如果测试用例测试源代码和测试编译代码不一致,则这个测试用例不可以删除
6)等价变异体
变异体与源代码语法不同,语义相同,则为等价变异体
3 测试方法
如果这个过程中,有减分,说明测试用例不完善或者出现重复的测试用例。
3. 工具
在变异测试中Java常用的工具为PITest,Python常用的工具为Mutpy,现在我们来学习一下Mutpy。
1)安装
在线安装
#pip install mutpy |
离线安装
#git clone git@github.com:mutpy/mutpy.git #cd mutpy/ #python setup.py install |
2)被测程序:calculator.py
def mul(x, y): return x * y
3)测试程序:test_calculator.py
from unittestimport TestCase from calculatorimport mul classCalculatorTest(TestCase): def test_mul(self): self.assertEqual(mul(2, 2), 4)
4)运行
root@ubuntu:/home/jerry/muttest# mut.py --target calculator --unit-test test_calculator -m [*] Start mutation process: - targets: calculator - tests: test_calculator [*] 1 tests passed: - test_calculator [0.00040 s] [*] Start mutants generation and execution: - [# 1] AOR calculator: -------------------------------------------------------------------------------- 1: def mul(x, y): - 2: return x * y + 2: return x / y -------------------------------------------------------------------------------- [0.01345 s] killed by test_mul (test_calculator.CalculatorTest) - [# 2] AOR calculator: -------------------------------------------------------------------------------- 1: def mul(x, y): - 2: return x * y + 2: return x // y -------------------------------------------------------------------------------- [0.01476 s] killed by test_mul (test_calculator.CalculatorTest) - [# 3] AOR calculator: -------------------------------------------------------------------------------- 1: def mul(x, y): - 2: return x * y + 2: return x ** y -------------------------------------------------------------------------------- [0.01048 s] survived [*] Mutation score [0.11673 s]: 66.7% - all: 3 - killed: 2 (66.7%) - survived: 1 (33.3%) - incompetent: 0 (0.0%) - timeout: 0 (0.0%) You have new mail in /var/mail/root
|
注:我在Win10下测试没有成功,在Linux下测试成功。
各位可以看到3个变异,存活了1个,杀死了22个,最后得分为66.7%。分析一下原因。
这里对于x * y的3个变异,分别为x / y ,x // y和x ** y。
在测试用例中x=2,y=2 ,测试结果为4 返回 True;
在变异x / y,测试结果为1 返回 False;
在变异x // y,测试结果为1 返回 False;
在变异x ** y,测试结果为2 返回 True。
所以当x=2,y=2变异x ** y是与源代码等价的。我们修改一下测试代码。
test_calculator.py
from unittestimport TestCase from calculatorimport mul classCalculatorTest(TestCase): def test_mul(self): self.assertEqual(mul(2, 3), 6)
分析一下:
源代码:2 * 3 = 6,返回True
对于x / y:2/3!= 6,False;
对于x // y:2//3 =0!= 6,False;
对于x ** y:2**3=8!= 6,False。
所以三个都被杀死了,得分为100%。
运行一下。
root@ubuntu:/home/jerry/muttest# mut.py --target calculator --unit-test test_calculator -m [*] Start mutation process: - targets: calculator - tests: test_calculator [*] 1 tests passed: - test_calculator [0.00050 s] [*] Start mutants generation and execution: - [# 1] AOR calculator: -------------------------------------------------------------------------------- 1: def mul(x, y): - 2: return x * y + 2: return x / y -------------------------------------------------------------------------------- [0.05670 s] killed by test_mul (test_calculator.CalculatorTest) - [# 2] AOR calculator: -------------------------------------------------------------------------------- 1: def mul(x, y): - 2: return x * y + 2: return x // y -------------------------------------------------------------------------------- [0.03214 s] killed by test_mul (test_calculator.CalculatorTest) - [# 3] AOR calculator: -------------------------------------------------------------------------------- 1: def mul(x, y): - 2: return x * y + 2: return x ** y -------------------------------------------------------------------------------- [0.04315 s] killed by test_mul (test_calculator.CalculatorTest) [*] Mutation score [0.20079 s]: 100.0% - all: 3 - killed: 3 (100.0%) - survived: 0 (0.0%) - incompetent: 0 (0.0%) - timeout: 0 (0.0%) |
得分果然是100%