组合测试
组合测试根据参数及其取值建立输入模型,再选择能够覆盖指定交互关系的测试集合。例如浏览器、语言和主题各有两种取值,全组合需要八项;如果目标只是覆盖任意两个参数的所有取值对,可能用更少的测试完成。
减少组合必须保留清楚的覆盖承诺。“成对覆盖完整”说明每对参数都组合过,不代表三个参数共同触发的问题也已覆盖,更不代表这些输入都得到了正确断言。
1. 面试核心
先记住这几条,再读正文- 成对测试覆盖任意两个参数的可行取值组合;推广到任意
t个参数就是t路交互覆盖。它们与全组合的要求不同。卡 · 成对覆盖示例 - 参数取值先根据规则和风险划分,模型没有包含的值或因素不会因覆盖率达到 100% 而自动得到验证。卡 · 输入模型与预期结果
- 约束会改变可行组合,删掉非法用例后必须重新计算覆盖,否则可能一起丢掉仍然合法的取值对。卡 · 约束与覆盖修复
- 成对覆盖可能遗漏三路及以上交互。关键业务组合、已知故障和边界应显式保留,不能为缩小数量而删除。卡 · 交互强度与风险
2. 成对覆盖示例
假设三个参数彼此独立:浏览器 A、语言 B、主题 C,每个参数暂时用 0 和 1 表示两种已选取值。下表只有四项:
| 用例 | A | B | C |
|---|---|---|---|
| T1 | 0 | 0 | 0 |
| T2 | 0 | 1 | 1 |
| T3 | 1 | 0 | 1 |
| T4 | 1 | 1 | 0 |
只看 A、B 两列,可以找到 00、01、10、11;只看 A、C 或 B、C,也能找到各自四种取值对。因此三对参数共 12 个覆盖目标都出现过。
但组合 A=1、B=1、C=1 没有出现。如果某个页面只在该浏览器、该语言和该主题同时启用时错位,四项成对测试完全可能通过。
以下代码直接核对这个结论:
from itertools import combinations, product
def pairs(rows):
return {
(i, j, row[i], row[j])
for row in rows
for i, j in combinations(range(3), 2)
}
all_rows = list(product([0, 1], repeat=3))
selected = [(0, 0, 0), (0, 1, 1), (1, 0, 1), (1, 1, 0)]
assert pairs(selected) == pairs(all_rows)
assert (1, 1, 1) not in selected
print(len(selected), len(pairs(selected)))
输出 4 12,分别是选择的用例数和覆盖的取值对数量。这个检查验证输入表的组合性质,没有执行任何浏览器,也没有验证页面结果。
3. 输入模型与预期结果
模型中的取值应代表有意义的差异。浏览器可能按内核、受支持版本和实际用户分布选择;语言可能考虑不同文字方向和长度;输入金额可以先按 等价类划分选择代表,再保留阈值附近的值。
“只选两种语言”不意味着所有语言都被覆盖。组合覆盖的分母只包含已经建模的因素和取值,报告时应同时给出模型、约束和交互强度。
生成输入后仍要准备预期结果。对每个浏览器、语言、主题组合,可能都要检查内容正确、交互可用和布局关键区域;只成功打开页面,不能证明该组合下的行为正确。业务规则由多个条件决定时,可以先用 判定表整理动作,再为选中的组合建立断言。
4. 约束与覆盖修复
假设新增约束:A=0 时不支持 B=1。T2 变成非法组合,如果简单删掉它,剩余三项会丢失两个仍然可行的取值对:A=0、C=1 和 B=1、C=1。
它们分别可以由 001 和 111 覆盖。下面的独立示例先从全部可行输入计算目标,再检查缺口:
from itertools import combinations, product
def pairs(rows):
return {
(i, j, row[i], row[j])
for row in rows
for i, j in combinations(range(3), 2)
}
def allowed(row):
return not (row[0] == 0 and row[1] == 1)
valid = [row for row in product([0, 1], repeat=3) if allowed(row)]
remaining = [(0, 0, 0), (1, 0, 1), (1, 1, 0)]
target = pairs(valid)
assert len(target - pairs(remaining)) == 2
repaired = remaining + [(0, 0, 1), (1, 1, 1)]
assert all(allowed(row) for row in repaired)
assert pairs(repaired) == target
print(len(repaired), len(target))
输出 5 11。不可行的 A=0、B=1 不再计入目标,另外 11 个取值对都覆盖到了。这是一种便于解释的修复,不声称是最少用例集合。
参数很多时,可使用支持约束的组合生成工具,再独立检查生成结果满足约束和覆盖目标。工具负责寻找组合,输入模型和业务取舍仍需由测试设计给出。
5. 交互强度与风险
成对覆盖适合压缩配置组合,但不能作为所有业务的统一结束条件。明确依赖三个条件的优惠规则,需要保留这三个条件的关键组合;涉及权限、金额和并发的高风险链路,也不能只凭成对表就判断充分。
可以在全局采用较低交互强度,对高风险因素提高强度,再加上已知缺陷的复现输入。已经出现过的 111 故障应该直接保留为回归用例,而不是期待下一次生成器碰巧选中它。
组合测试也不自动覆盖事件顺序。若同一组操作的先后影响结果,应使用 状态迁移测试定义序列目标;配置表里出现了“取消”和“支付”两个值,不等于执行过取消后支付的链路。
6 · 面试题卡片
本页题目与参考回答
6.1 成对测试覆盖了什么,与全组合有什么区别?
考察重点:参数对、取值对与覆盖强度。
相关内容:成对覆盖示例。
参考回答
成对测试要求任意两个参数的每个可行取值组合至少出现一次,全组合则执行全部参数的所有可行组合。 一个测试可以同时覆盖多对参数,因此成对集合可能更小。
三参数各两值的四行示例能覆盖全部取值对,却没有包含 111。只有三个因素同时触发的问题可能因此漏测。
6.2 组合覆盖达到 100%,为什么仍不能说明功能正确?
考察重点:建模范围、输入覆盖与结果判定。
相关内容:输入模型与预期结果。
参考回答
覆盖率只针对已定义的因素、取值、约束和交互强度,而且只说明输入组合出现过。 未纳入的浏览器版本、特殊字符或更高阶交互仍可能遗漏。
执行这些输入还需要正确断言。页面能打开不代表关键交互正常,生成组合不能代替建立预期结果。
6.3 从成对测试表里删除非法组合,为什么要重新检查覆盖?
考察重点:受约束目标与一行覆盖多个参数对的关系。
相关内容:约束与覆盖修复。
参考回答
被删的一行可能是某些合法取值对的唯一载体。 删除非法完整组合,不代表该行里的每一对参数都非法。
应从全部可行配置定义覆盖目标,再检查剩余集合缺少哪些取值对,用可行输入补齐,或重新生成支持约束的组合表。
6.4 已知某故障必须三个条件同时成立,成对测试还够吗?
考察重点:已知风险与覆盖强度的选择。
相关内容:交互强度与风险。
参考回答
不够,必须显式保留这个三条件组合,并考虑提高相关因素的交互强度。 成对集合可能包含它,也可能完全没有包含,不能依赖偶然选择。
回归用例应直接保护已知触发条件。全局组合压缩只用于其他范围,不应删掉已经有明确价值的故障样本。
7. 参考资料
来源:Coding 101 原文 · 免费阅读,引用请保留出处。内容使用说明