什么是突变测试?(示例)
什么是突变测试?
突变测试 变异测试是一种软件测试方法,它通过修改(或称变异)源代码中的某些语句,来检验测试用例是否能够发现源代码中的错误。变异测试的目标是确保测试用例的鲁棒性,使其在面对变异后的源代码时能够失败。
对变异程序所做的更改必须非常小,以免影响程序的整体目标。变异测试也称为基于缺陷的测试策略,因为它涉及故意在程序中创建缺陷。它是一种…… 白色 Box 测试与验证 主要应用于 单元测试.
变异测试最早由 Richard Lipton 于 1971 年在一篇学生论文中提出,并于 1978 年由 DeMillo、Lipton 和 Sayward 在论文《测试数据选择提示》中正式阐述。由于当时的计算成本较高,变异测试一度发展受阻,但此后随着诸如……等编程语言的出现,它又重新获得了关注。 Java,C#, Python, Java脚本和 XML。
如何执行变异测试?
以下是执行变异测试(也称为变异分析)的步骤:
第三步: 通过创建多个称为变异体的版本,可以在程序源代码中引入缺陷。每个变异体都应包含一个缺陷,目标是使变异体版本运行失败,从而验证测试用例的有效性。
第三步: 测试用例应用于原始程序和变异程序。 测试用例 应该是足够的,并且它被调整来检测程序中的错误。
第三步: 比较原始程序和变异程序的运行结果。
第三步: 如果原始程序和变异程序产生不同的输出,则变异程序会被测试用例判定为错误。因此,该测试用例足以检测出原始程序和变异程序之间的差异。
第三步: 如果原始程序和变异程序产生相同的输出,则变异程序得以存活。在这种情况下,需要创建更有效的测试用例来杀死所有变异程序。
下图 trac从最初的程序到变异体的产生,再到最终的死亡或存活判决,都遵循相同的五个步骤。
如何创建变异程序?
变异是指对程序语句进行的一次语法更改。每个变异后的程序与原始程序之间应该恰好只有一个语法更改。
| 原创节目 | 变异程序 |
| 如果 (x>y) 打印“你好” 其他 打印“嗨” |
如果(x 打印“你好” 其他 打印“嗨” |
在上面的示例代码中,只有比较运算符发生了变化,但当 x 大于 y 时,测试用例现在会输出“Hi”而不是“Hello”。图示展示了这一语法上的修改。
突变程序中需要改变什么?
有多种技术可用于生成变异程序。以下三个类别涵盖了大多数工具自带的变异算子。
| Operand 替换运算符 | 表达式修改运算符 | 语句修改运算符 |
| 将操作数替换为另一个操作数(x 替换为 y,或 y 替换为 x),或者替换为常量值。 | 在程序语句中替换运算符,或插入新运算符。 | 修改程序语句以创建变异程序。 |
| 计费示例: 如果 (x>y) 则替换 x 和 y 值 如果(5>y)用常数 5 替换 x |
计费示例: 如果(x==y) 我们可以将 == 替换为 >=,并将变异程序表示为 如果(x>=y)并在语句中插入++ 如果(x==++y) |
计费示例: 删除 if-else 语句中的 else 部分 删除整个if-else语句,以检查程序的运行情况。 |
一些变异算子的示例:
- GOTO 标签替换
- 退货声明替换
- 语句删除
- 一元运算符插入(例如 - 和 ++)
- 逻辑连接器更换
- 可比较数组名称替换
- 删除 if-else 语句中的 else 部分
- 添加或替换运算符
- 通过更改数据进行语句替换
- 变量的数据修改
- 程序中数据类型的修改
Opera与边界条件相交的拓扑结构最常存活,因此突变结果通常指向边界条件的缺口。 边值分析.
突变测试的类型
In 软件工程变异测试从根本上分为三种类型——语句变异、值变异和决策变异。
- 语句修改 – 语句被剪切、粘贴或删除,因此结果可能是删除一些代码行。
- 价值变异 – 修改主要参数和常量的值,例如改变循环边界或阈值。
- 决策突变 – 控制语句发生更改,例如翻转ping 关系运算符或否定条件。
工具会将它们的操作符归入这三个类别,因此,产生存活变异体的操作符家族可以告诉测试人员缺少哪种类型的断言。存活的决策变异体通常标记一个未经测试的分支,这与……重叠。 循环测试.
突变测试的自动化
手动执行变异测试极其耗时且复杂,因此建议使用自动化工具,这样也能降低成本。变异测试工具会编译变异体,安排运行,记录每次测试失败导致哪个变异体被杀死,并报告测试得分。
可用工具列表:
- 史赛克 — 一个开源的变异测试框架,提供多个版本 Java脚本和 TypeScript (StrykerJS)、C# 和 .NET (Stryker.NET) 以及 Scala (Stryker4s)。
- 基坑也写作 PITest——一种用于突变检测的系统 Java 以及修改编译后的字节码并接入 Maven 的 JVM 和 Gradle 与……并肩而建 JUnit.
两者都作为构建步骤运行,因此它们属于同一步骤。 持续整合 管道其余部分 自动化测试 套房。
突变评分
突变得分定义为被杀死的突变体占突变体总数的百分比。
突变分数 = (杀死的突变体/突变体总数) * 100
下面显示的是大多数工具报告的公式形式。
当测试用例的得分达到 100% 时,则认为其变异性已得到充分满足。实际上,分母必须排除某些特定用例。 等效突变体 — 语法改变后行为与原代码完全相同的变异体,因此任何测试都无法将其判定为无效。因此,工具会将判定为无效的变异体数量除以无效变异体总数加上存活的非等效变异体总数,并允许测试人员标记等效变异体。
实验结果表明,变异测试是衡量测试用例充分性的有效方法。其主要缺点在于生成变异体以及针对每个变异体执行所有测试用例的成本较高。
突变检测与 Code 保障范围
高 测试覆盖率 并不能证明测试的有效性。行覆盖率和分支覆盖率记录的是哪些语句执行了,而不是之后是否进行了任何验证,因此,即使测试调用了一个方法但什么也没断言,它仍然会被计为覆盖。变异测试弥补了这一缺陷,因为只有当断言实际失败时,变异体才会被终止。
| 方面 | Code 覆盖 | 突变评分 |
| 它测量什么 | 测试执行了哪些代码行或分支? | 哪些注入故障被检测到 |
| 对断言很敏感 | 不——即使测试没有断言,它仍然可以增加覆盖率。 | 是的——只要没有断言失败,变异体就能存活下来。 |
| 运行成本 | 一次仪器化测试运行 | 每个存活的变异体只进行一次测试,目前速度较慢。 |
| 典型用途 | 每次提交时都要快速设置一个门控 | 对关键模块进行更深入的定期检查 |
| 故障模式 | 100%覆盖率,但未经任何实际验证 | 无法被杀死的同类变异体 |
这两个指标是互补的。覆盖率指的是从未被执行过的代码;变异分数指的是已执行过但从未被检查过的代码。两者都基于相同的信息。 缺陷管理流程同时采取以下措施: 缺陷密度.
突变测试的优势
以下是突变测试的优点:
- 这是实现对源程序高覆盖率的有效方法。
- 它测试测试套件本身,而其他任何测试套件都无法做到这一点。 软件测试技术 直接执行。
- 变异测试为软件开发人员带来了良好的错误检测能力。
- 该方法能够发现源代码中的歧义,并有可能暴露出普通运行永远无法触及的错误。
- 幸存的变异体是可以采取行动的:每个变异体都指出了该套件未能注意到的特定行和特定更改。
- 客户通过这项测试将获得更可靠、更稳定的系统,从而受益匪浅。
突变测试的缺点
另一方面,突变检测也存在以下缺点:
- 变异测试成本极高且耗时,因为需要生成和编译大量的变异程序。
- 由于这项测试非常耗时,可以说如果没有自动化工具,这项测试是无法完成的。
- 每个变异体都要经过与原始程序相同数量的测试用例,因此必须对整个测试套件运行大量的变异体。
- 同种变异体无法通过任何测试杀死,通常需要人工审核才能将其与真正的幸存者区分开来。
- 由于该方法会改变源代码,因此不适用于 黑色 Box 测试与验证.
何时使用突变检测
上述成本分析表明,变异测试很少会在每次提交代码时都对整个代码库进行测试。它的优势在于,当未检测到的缺陷代价高昂,且被测代码量较小,能够快速发生变异时,变异测试才能真正发挥作用。
- 安全关键或财务逻辑 — 付款计算、税收规则和授权检查,其中,一个无声的错误答案比崩溃更糟糕。
- 套房覆盖率异常高 — 当覆盖率接近 100% 时,缺陷仍然存在。
- 正在重构遗留代码 — 突变结果揭示了现有测试是否能够发现退化现象。
- 库和共享组件 — 重复使用中的故障 元件 乘以每个呼叫者。
- 团队练习 测试驱动的开发 — 该分数检验的是,首先编写的测试是否真正有效。
通常情况下,在一次性原型、缺乏分支逻辑的薄弱代码或生成的代码,或者以慢速测试为主的测试套件上运行测试是不值得的。 整合测试 单程就需要几个小时。
因此,大多数团队会将运行范围限定在已更改的文件上,为重要的模块设置阈值,并允许更广泛的操作进行。 回归测试 套房承载着其余部分 软件测试生命周期.



