重新认识软件测试:从二度人脉设计测试用例
本页目录
2013 年和朋友做项目时,我们需要测试一个“二度人脉”功能。我先画了一张有 16 个用户的关系图,逐个列出每个人能够查到的联系人,随后发现很多结果相似,便想减少测试。
原稿有一个重要问题:减少测试起点,与删除关系图中的节点,是两件不同的事。删掉节点会改变路径和预期结果,不能因为一些结果看起来重复,就认为缩小后的图与原图等价。这次迁移保留这个案例,重新整理测试设计。
先说清楚“二度”
“朋友的朋友”至少有两种解释:恰好走两条边能到达的人,或最短距离不超过两条边的人。是否包括直接朋友、是否排除自己,也会影响答案。
这里采用明确的示例规则:关系是无向的;返回与起点最短距离为 1 或 2 的全部用户;不返回自己;同一用户只返回一次。生产系统如果采用单向通讯录关系,就必须另写规则与预期,不能直接沿用这个模型。
电话号码只是属性,不影响这里的图遍历逻辑,所以测试使用 A、B、C 等稳定标识。先检查成员集合,若产品还要求特定排序,再单独检查顺序。
用小图覆盖不同错误
表中 A-B 表示一条无向边。未出现在边中的起点仍是图中的独立节点。
| 场景 | 输入边 | 起点 | 预期集合 | 检查什么 |
|---|---|---|---|---|
| 孤立节点 | 无 | A | 空集 | 没有邻居时不返回自己 |
| 一条边 | A-B | A | B | 直接朋友被包括 |
| 长链 | A-B、B-C、C-D | A | B、C | 两跳停止,不带入 D |
| 三角形 | A-B、B-C、C-A | A | B、C | 环路不带回自己 |
| 菱形 | A-B、A-C、B-D、C-D | A | B、C、D | 多条路径到同一用户只返回一次 |
| 不连通图 | A-B、C-D | A | B | 不带入另一个连通分量 |
对于重复关系、自环、未知用户,还应明确输入协议:拒绝、清洗还是允许。本文代码用集合存储邻居,重复边自然合并;自环不会使自己进入结果;查询不存在的用户则抛出异常。这些只是本例的约定。
保留一个容易检查的参考实现
优化后的实现可能有缓存、索引或复杂的数据访问。可以用一个简单的广度优先搜索作为小规模测试的参考,比较完整结果集合:
from collections import deque
def within_two_hops(graph, start):
if start not in graph:
raise KeyError(start)
distance = {start: 0}
queue = deque([start])
while queue:
current = queue.popleft()
if distance[current] == 2:
continue
for neighbor in graph[current]:
if neighbor not in distance:
distance[neighbor] = distance[current] + 1
queue.append(neighbor)
return set(distance) - {start}
graph = {
"A": {"B", "C"},
"B": {"A", "D"},
"C": {"A", "D"},
"D": {"B", "C"},
}
assert within_two_hops(graph, "A") == {"B", "C", "D"}
这里假定邻接表完整:每个邻居都存在于字典中,而且每条无向边都在两端记录。输入校验属于另一层测试,不能把缺失邻接表产生的异常当作业务结果。
怎样减少测试才有依据
如果保留原来的 16 人图,只挑部分用户作为查询起点,图本身不变。这是减少测试查询,需要说明选中的起点覆盖了哪些结构,而不是只看返回数量是否一样。两个起点都返回 5 个人,也可能经过完全不同的路径。
如果把大图换成几张小图,就是重新设计测试输入。小图应有独立的预期结果,分别针对环、深度限制、去重、孤立点等错误;它们不是原图的无损压缩。原始大图仍可以作为集成回归用例保留。
除了例子,还能检查一些普遍性质:结果不包含自己;改变邻接列表顺序不影响结果;重复添加同一条边不影响结果;一致地重命名节点只会相应地重命名答案。再用小规模随机图比较优化实现和参考实现,能发现手写用例之外的遗漏。
软件测试的目标不是把用例数量减到最少,而是让每个用例对应一个清楚的行为或风险,同时让失败容易解释。小而明确的输入,通常比一张很大却说不清覆盖了什么的图更容易维护。
相关文章
- GeoHash 指南2016-09-07
- SuperMemo 与 Anki:间隔重复算法笔记2014-06-04
- 【Google软件测试之道】靠谱团队学2025-07-23
- Fast Unfolding:用模块度发现社群2018-09-16