变异测试在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个变异体。
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%