猿创征文|聊聊“高大上“的变异测试(Mutation Testing)

本文介绍了一种基于故障注入的测试技术——变异测试,包括其基本概念、实施步骤及优缺点,并通过JavaScript示例展示了如何利用变异测试提高测试用例的质量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、变异测试简介

变异测试是一种基于故障注入的测试技术将错误代码插入到被测代码中,以验证当前测试用例是否可以发现注入的错误。我认为该测试手段理论上属于白盒测试范畴。

变异测试的主要目的是为了验证测试用例的有效性,在注入变异后,测试用例能发现该错误,则表明用例有效的;反之,表明测试用例是无效的,需要补充该变异的测试用例。

变异测试有助于评估测试用例的质量,以帮助测试人员编写更有效的测试用例。测试人员设计的测试用例发现的变异体越多,表明其设计的测试用例质量就越高。

a853399181730a9091f8b169acfb2348.png

在深入理解变异测试之前,让我们先搞懂和它相关的几个核心概念。

1) 变异

可以理解为对源代码的任何更改,也可以理解为引入的故障。

2) 变异体

可以理解为被测代码的变异版本,即已经在被测代码中注入变异的代码。当测试用例在变异体版本的代码运行时,理论上该测试用例执行的结果应该与原被测代码执行的结果不同。

  • 存活的(survived)变异体: 变异注入的错误并不能被测试用例感知,这种情况称为变异体能够“存活”,说明测试用例的有效性存在问题,需要对测试用例进行补充和修正。
  • 杀死的(killed)变异体: 在变异体代码上执行测试不通过,说明变异注入的错误能够被测试用例T感知到,测试用例能够“杀死”此变异,说明此测试用例是有效的。
  • 等价的变异体:这个其实也很好理解,通过下面例子介绍下。
源代码:
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开始,经历1,2,3,4,5,6,7,8,9到10,在源代码中由于10<10返回False,退出循环;在变体1中由于10!=10返回False,退出循环。

3) 变异分

变异分也称为变异充分性,这是基于测试用例发现变异体数量计算出来的分数,计算公式如下:

528473dff3281397283e3e680668d677.png

注意,在计算变异分时不会考虑等价的变异体。

二、如何进行变异测试

下面通过JavaScript代码做演示,使用Jasmine测试框架写单测用例。

1.编写原被测代码

const user_info = () =&amp;amp;gt; {
  mother_age = parseInt(prompt("Enter mother's age"))
  daughter_age = parseInt(prompt("Enter daughter's age"))

  if (mother_age &amp;amp;gt; daughter_age) {
    alert(`Daughter's age is ${daughter_age}. Mother's age is
    ${mother_age}. Welcome to the Mother-Daughter program`)

  } else {   
    alert(`Daughter's age: ${daughter_age}, is more than mother's age: ${mother_age}.
    Please enter correct ages`)      
  }
}
user_info();

2.编写 Jasmine 单元测试用例

describe("User", function() {
  it("should compare the two numbers from user input", function(){
    expect(20).toBeGreaterThan(5);
  })
});

 

3.在原始代码运行测试,以确保测试用例都是通过的

4c64812223b93843b51e2de2608b70e0.png

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)

 

e97f1492c51e827e1d3f2c355e7aef56.png

5.在变异版本上运行测试

448aa275443340f230be5ddb163efb18.png

Daughter's age: 5, is more than mother's age: 20. Please enter correct ages

6.比较源代码和变异版本代码运行测试的结果

通过运行结果发现,虽然二者使用相同的测试用例,但是运行结果是不一样的,因此,我们的测试用例“杀死”了变异,也说明我们的测试用例是有效的。

三、变异测试类型

1) 值变异

通过改变参数值来实现代码变异,例如在原值基础上+/- 1。

原始代码

f2b69442eda86e04c2b3b4c231330221.png

上面的代码逻辑是将i<4的偶数相乘,那么通过值变异可以将初始化值由let i=0更改为let i=1

变异代码

c3e52659e59e8b2d2394431ea686b6fd.png

2) 语句变异

通过删除、重复或颠倒代码块中的语句实现代码变异。

例如,在 if-else 语句块中,重复console.log代码。

原始代码

8e64b455a50e0c1e988d1fb7534b9a12.png

变异代码

c561e7596fec512cf9e8514d448d9c76.png

3) 运算符变异

通过修改代码中的运算符来实现代码变异,例如常见的值比较逻辑。我们可以将> 更改为 <

 

原运算符

运算符变异

1

<=

>=

2

>=

==

3

===

==

4

and

or

5

||

&&

四、变异测试优缺点分析

优点:

  • 可以覆盖大部分代码逻辑。
  • 可以测试特定部分代码,而不仅仅是通过路径、分支或语句方式实现。
  • 可以帮助我们评估测试用例的质量并进行优化。

缺点:

  • 变异测试需要在单元测试已经做得比较完备的基础上才有其价值。

五、变异测试工具推荐

Stryker、Jumble、PIT 和 Insure++。

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软件质量保障

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值