期末(面向及格复习)
软件测试基本概念
简单了解即可 只考选择题
测试用例

测试报告

软件测试分类

灰盒测试通过一些表征性的现象或事件来反映内部运行状态。灰盒测试使用特定方法和工具来提取应用程序的内部
知识和交互信息,进而使用内部间接信息来设计测试,以提高测试效率(比如利用数据库结构这一内部信息来验证系统的内部状态是否正确)
测试分析框架


BUG理论
PIE模型
Bug在程序的不同阶段,分别称为Fault, Error和Failure,定义如下: ➢ Fault-故障:是指静态存在于程序中的缺陷代码; ➢ Error-错误:是指程序运行缺陷代码后导致错误的程序状态; ➢ Failure-失效:是指程序错误状态传播到外部被感知的现象。
错误不一定会传播到输出!即,中间状态的出错不一定导致程序失效。
反向定义及特性

多样性测试
随机测试
简单随机测试
针对输入空间 ,随机测试以概率分布 进行随机抽样获得测试集
自适应随机测试(会出一条应用题 考距离如何测量)ART Adaptive Random Testing
自适应随机测试以概率分布 进行随机抽样并结合距离度量反馈信息获得测试集 T


引导性随机测试 了解即可
选择适当的测试生成方法,快速生成测试覆盖程序的各种路径或指定路径。
用符号执行探索程序路径并生成路径约束条件 最后求解这些约束条件得到程序的测试输入范围
等价类测试方法
必出应用题 需要掌握流程



组合测试

故障假设测试
边界故障假设
黑盒故障假设

白盒故障假设

变异缺陷假设
需要知道基本概念和两个假设

变异分析(Mutation Analysis),也称为变异测试,是一种用于评估测试集有效性和充分性的技术。它通过对原程序进行微小的语法修改来生成“变异体”,并检查现有的测试集能否发现这些人为注入的缺陷。
以下是其核心概念:
- 变异体 (Mutant)
- 定义:变异体 $P’$ 是对原程序 $P$ 进行符合语法规则的微小代码修改后生成的程序版本。
- 特征:
- 微小修改:模拟程序员可能犯的简单错误(基于“熟练程序员假设”)。
- 符合语法:确保变异体能够通过编译并运行。
- 预期差异:理论上期待测试 $t$ 在运行变异体 $P’$ 时,其行为(输出)与原程序 $P$ 不一致。
- 变异体检测 (Mutant Detection)
- 定义:也称为“杀死”变异体。如果测试用例 $t$ 使得变异体 $P’$ 的运行结果与原程序 $P$ 不同(即 $P’(t) \neq P(t)$),则称测试 $t$ 检测(杀死)了变异体 $P’$。
- 存活变异体:如果变异体在测试下通过(即结果与原程序一致),则称该变异体是存活的。
- 检测分类:
- 弱杀死 (Weak Kill):变异体的中间状态与原程序不同。
- 强杀死 (Strong Kill):变异体的最终输出结果与原程序不同(这是传统意义上的变异分析)。
- 变异分数 (Mutation Score)
-
定义:变异分数是评估测试集质量的重要度量指标。
-
计算公式:
\[变异分数 = \frac{杀死的变异体个数}{变异体总数 (n)}\] -
意义:测试集杀死的变异体越多,分值越高,代表测试集的质量越高。
- 等价变异体 (Equivalent Mutant)
- 定义:如果不存在任何测试用例能够杀死变异体 $P’$,则称 $P’$ 是 $P$ 的等价变异体。
- 特点:它在语法上与原程序不同,但在语义上完全等价(即对于任意输入,输出行为都相同)。
- 难点:在实践中很难判定一个变异体是否绝对等价,这会影响变异分数的准确性。
- 变异算子 (Mutation Operator)
- 定义:变异算子定义了生成变异体的规则(在符合语法规则的前提下)。
- 常见类型:
- 值变异:改变常量或参数的值(例如 $x=5$ 变为 $x=10$)。
- 语句变异:增加、删除、替换语句(例如 $total=x-y$ 变为 $total=x+y$)。
- 决策变异:改变逻辑或算术运算符(例如 $x>y$ 变为 $x<y$)。
- 经典算子:如 ABS(绝对值插入)、AOR(算术操作符替换)、ROR(关系运算符替换)等。
变异测试优化技术
变异测试的主要挑战在于生成和执行的变异体数量巨大,导致成本高昂。优化的目标是找到一个较小的变异体集合,使其对测试集的评估能力近似于完整集合。
主要有以下三种优化方法:
- 变异算子选择 (Mutation Operator Selection)
- 策略:不使用所有可能的算子,而是从经验上相似的算子中选择具有代表性的变异算子。
- 强变异算子:基于“强弱”关系进行选择。如果杀死算子 $O_1$ 生成的变异体必然能杀死算子 $O_2$ 生成的变异体,则认为 $O_1$ 强于 $O_2$,应优先选择强变异算子。
- 变异体随机选择 (Mutant Random Selection)
- 策略:从生成的变异体池中,基于经验进行均匀随机抽样,仅执行抽取到的样本。
- 原理:通过统计学原理,用一部分变异体的得分来估算整体的变异分数。
- 变异体聚类抽样 (Mutant Cluster Sampling)
- 策略:先对变异体进行聚类分析,然后从每个类中进行随机抽样。
- 原理:
- 相似性:如果两个变异体对于大多数测试的被杀死情况是相同的,则认为它们是相似的(冗余的)。
- 通过聚类识别相似变异体,去除冗余,只保留代表性变异体,从而在降低成本的同时保持评估的准确性。
逻辑故障假设
软件逻辑故障是指在程序代码中存在的逻辑错误或不当的设计,导致程序无法按照预期执行或出现异常行为
我们不盲目地乱测,而是假设程序里的错误就是“把AND写成OR”、“写错变量名”、“条件永远为真/假”这几种。并且利用“擒贼先擒王”的策略(故障强弱关系),优先检测最复杂的逻辑错误,从而用最少的测试次数,揪出最多Bug
图分析测试

控制流图 必考



数据流图 选择题

事件流图 选择题

图结构测试方法
主路径测试



基本路径测试

图元素测试
数据流测试

逻辑测试 重点需要掌握




开发者测试
单元测试

接口测试
接口测试是指针对软件系统中的接口进行的测试,以验证接口是否能够按照预期进行通信和数据交换
集成测试
集成测试旨在验证软件组件的协同工作能力,并检测系统功能性和性能问题
多样性开发者测试 需要知道使用覆盖的原因 为什么使用覆盖而非错误率
也称为变异测试,通过修改源代码生成变异体,然后运行测试集,观察测试集是否能够检测到变异体中的缺陷
代码多样性:也就是常说的代码覆盖测试,通过遍历程序的逻辑结构,评估测试用例对代码的覆盖程度。它要求测试满足特定的覆盖标准,如语句覆盖、分支覆盖等

故障假设开发者测试
测试粒度、测试优势:
| 测试类型 | 测试粒度 (测试范围) | 测试优势 |
|---|---|---|
| 单元测试 (Unit Testing) | 最细粒度 (组件/代码块) 针对应用程序代码分解后的最小单元(如函数、类、方法)进行测试。 | 1. 尽早发现问题:在开发周期早期进行,大幅降低修复成本。 2. 提高代码质量:验证代码的正确性、清晰性、规范性和高效性。 3. 确保可测试性:辅助 TDD 开发,帮助设计可测试的代码结构。 4. 作为代码审查:提供额外视角,确保代码在首次编写时就是健壮的。 |
| 接口测试 (Interface/API Testing) | 中等粒度 (通信/数据交换) 位于单元测试和 GUI 测试之间,针对软件系统中的接口,验证通信和数据交换。 | 1. 反馈及时:提供即时反馈,适合敏捷开发和快速迭代。 2. 效率高:测试速度快于系统级 GUI 测试,且业务覆盖率高。 3. 保障稳定性:确保组件间数据传输的正确性和安全性,修复缺陷以保障软件可靠性。 |
| 集成测试 (Integration Testing) | 较大粒度 (组件组装/协作) 在单元测试之后,验证模块组装后的协同工作能力。 | 1. 发现接口缺陷:检测单元测试无法发现的模块间接口调用错误。 2. 解决数据问题:发现跨模块传输中的数据丢失、阻塞或格式错误。 3. 检测并发问题:发现模块同步运行时的死锁、资源竞争等问题。 |
| 多样性开发者测试 (Diversity Testing) | 逻辑结构粒度 (路径/覆盖率) 基于代码覆盖(如语句、分支),遍历程序的逻辑结构和执行路径。 | 1. 评估测试完备性:通过覆盖率标准评估测试用例对代码的遍历程度。 2. 深入理解逻辑:结合路径分析和路径谓词,帮助开发者更好地理解程序逻辑。 3. 提高测试质量:利用约束求解生成特定路径的输入,发现深层缺陷。 |
| 故障假设开发者测试 (Fault Hypothesis Testing) | 故障模拟粒度 (变异体/缺陷) 基于假设的故障(如人工注入的变异体),在源码级别进行微小修改以模拟缺陷。 | 1. 评估测试集有效性:通过能否检测到变异体(杀死变异体),验证测试集发现缺陷的能力。 2. 暴露测试不足:主动暴露测试集的盲区,指导优化测试数据集。 3. 模拟潜在故障:模拟逻辑错误或程序崩溃等场景,提升测试覆盖率。 |
功能测试
确定功能需求-设计测试用例-执行测试与验证
多样性功能测试 和前面结合考
- 等价类划分
- 因果图分析

- 类别划分

故障假设功能测试
- 测试规划与分析 2. 测试实施方法 3. 测试评估与改进
性能测试
性能测试是一种评估软件或系统在实际运行环境中表现的方法,通过模拟真实用户行为,评估系统在响应时间、吞吐量和资源利用率等指标的表现,确保其满足预定的性能需求。利用负载模拟和压力测试,分析系统在不同负载下的表现,发现并解决性能瓶颈,优化资源使用效率,保障系统高效运行



适配测试




模糊测试





设计变异算子 重点
变异算子的设计决定了如何从旧种子产生新输入,目标是保持输入结构的有效性同时探索边界情况。
变异策略分类
- 基于变异 (Mutation-based): 对比特流进行随机或启发式的修改。
- 基于生成 (Generation-based): 利用文法规则 (Grammar) 或数据模型 (Data Model) 生成结构化输入。
常见变异算子 (以AFL为例)
AFL (American Fuzzy Lop) 使用了一套经典的确定性和随机性变异算子:
- Bitflip (位翻转):
bitflip L/S: 以步长 S 翻转 L 个比特位(如翻转 1bit, 2bits, 4bits)。
- Arithmetic (算术运算):
arith L/8: 对字节进行加减小整数的操作(如 8bit, 16bit, 32bit 整数加减)。
- Interest (有趣值替换):
interest L/8: 将字节替换为已知的“有趣数”(如溢出边界值 -1, 0, MAX_INT 等)。
- Havoc (大肆破坏):
- 对输入进行大量随机的综合变异(堆叠多种算子),产生较大的破坏。
- Splice (拼接):
- 随机拼接两个不同种子的一部分,试图组合出新的路径。
高级设计思路
- 语义感知 (Semantics-Aware): 分析程序中的语义检查(如 Magic Number 检查、Checksum 校验),识别影响语义的字段,针对性地设计变异策略以通过检查 (例如 SLF 工具)。
- 基于文法 (Grammar-based): 使用概率文法 (Probabilistic Grammar) 描述输入分布,甚至通过学习现有输入的分布来进行“反向”生成(互补文法),以生成能够触发异常的边缘输入。
设计有效反馈
反馈机制是模糊测试的“眼睛”,用于引导测试向更有价值的方向探索。
反馈类型 (根据运行时信息)
- 黑盒 (Blackbox):
- 反馈: 仅依赖输入/输出状态(如是否崩溃、响应时间)。
- 特点: 效率高但引导性弱,无法感知内部执行状态。
- 白盒 (Whitebox):
- 反馈: 利用污点分析 (Taint Analysis) 或符号执行 (Symbolic Execution) 获取精确的程序约束。
- 特点: 引导极强,能通过复杂检查,但开销巨大,扩展性差。
- 灰盒 (Greybox) - 主流方向:
- 反馈: 轻量级代码插桩 (Instrumentation)。
- 核心指标: 代码覆盖率 (Code Coverage)(如边覆盖、基本块覆盖)。
- 其他指标: 线程状态、堆内存状态等。
如何设计有效的反馈机制
- 覆盖率引导 (Coverage-guided):
- 如果一个输入触发了新的路径(New Edge/Path),则认为该输入是“有趣的” (Interesting/Valuable),将其保存为新种子。
- 马太效应: 好的种子能演化出更好的后代。
- 种子优先级排序 (Seed Prioritization):
- 距离计算 (Distance): 在定向模糊测试 (Directed Fuzzing) 中,计算种子执行路径与目标漏洞代码的距离。距离越近,优先级越高。
- 离群点优先: 优先选择覆盖率分布上独特的、偏离常规的种子。
- 能量调度 (Power Schedule):
- 根据反馈动态调整每个种子的变异次数。覆盖率高、执行路径罕见或距离目标近的种子,获得更多变异机会。
- 程序平滑 (Program Smoothing):
- 在基于梯度的模糊测试中(如 Neuzz),利用神经网络拟合程序行为,解决覆盖率反馈不连续的问题,利用梯度下降来指导变异方向。
测试用例优先级







