一、变异测试简介
变异测试的主要目的是为了验证测试用例的有效性,在注入变异后,测试用例能发现该错误,则表明用例有效的;反之,表明测试用例是无效的,需要补充该变异的测试用例。
变异测试有助于评估测试用例的质量,以帮助测试人员编写更有效的测试用例。测试人员设计的测试用例发现的变异体越多,表明其设计的测试用例质量就越高。
在深入理解变异测试之前,让我们先搞懂和它相关的几个核心概念。
1) 变异
可以理解为对源代码的任何更改,也可以理解为引入的故障。
2) 变异体
可以理解为被测代码的变异版本,即已经在被测代码中注入变异的代码。当测试用例在变异体版本的代码运行时,理论上该测试用例执行的结果应该与原被测代码执行的结果不同。
存活的(survived)变异体:变异注入的错误并不能被测试用例感知,这种情况称为变异体能够“存活”,说明测试用例的有效性存在问题,需要对测试用例进行补充和修正。
杀死的(killed)变异体: 在变异体代码上执行测试不通过,说明变异注入的错误能够被测试用例T感知到,测试用例能够“杀死”此变异,说明此测试用例是有效的。
等价的变异体:这个其实也很好理解,通过下面例子介绍下。
源代码:
其中变体1与源代码是等价的:都是i从0开始,经历1,2,3,4,5,6,7,8,9到10,在源代码中由于10<10返回False,退出循环;在变体1中由于10!=10返回False,退出循环。
3) 变异分
变异分也称为变异充分性,这是基于测试用例发现变异体数量计算出来的分数,计算公式如下:
注意,在计算变异分时不会考虑等价的变异体。
二、如何进行变异测试
下面通过JavaScript代码做演示,使用Jasmine测试框架写单测用例。
1.编写原被测代码
2.编写 Jasmine 单元测试用例
3.在原始代码运行测试,以确保测试用例都是通过的
Daughter's age is 5. Mother's age is 20. Welcome to the Mother-Daughter program
4.注入变异
我们将">"运算符 (mother_age >daughter_age) 更改为"<"运算符 (mother_age <daughter_age)
5.在变异版本上运行测试
Daughter's age: 5, is more than mother's age: 20. Please enter correct ages
6.比较源代码和变异版本代码运行测试的结果
通过运行结果发现,虽然二者使用相同的测试用例,但是运行结果是不一样的,因此,我们的测试用例“杀死”了变异,也说明我们的测试用例是有效的。
三、变异测试类型
1) 值变异
通过改变参数值来实现代码变异,例如在原值基础上+/- 1。
原始代码:
上面的代码逻辑是将i<4的偶数相乘,那么通过值变异可以将初始化值由let i=0更改为let i=1。
变异代码:
2) 语句变异
通过删除、重复或颠倒代码块中的语句实现代码变异。
例如,在 if-else 语句块中,重复console.log代码。
原始代码:
变异代码:
3) 运算符变异
通过修改代码中的运算符来实现代码变异,例如常见的值比较逻辑。我们可以将> 更改为 <。
四、变异测试优缺点分析
优点:
·可以覆盖大部分代码逻辑。
· 可以测试特定部分代码,而不仅仅是通过路径、分支或语句方式实现。
· 可以帮助我们评估测试用例的质量并进行优化。
缺点:
· 变异测试需要在单元测试已经做得比较完备的基础上才有其价值。
五、变异测试工具推荐
Stryker、Jumble、PIT 和 Insure++。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理