上篇写了游戏测试第一章——基础理论部分。这篇进入教程第二章:人工测试

这一章讲人工测试不只是”点点点”,有一整套测试思维工具可以学习。
以下是我理解后的梳理。


一、探索性测试:有限时间内的创造性测试

专业的探索性测试有严格的框架。教程管它叫会话式测试

核心规则就一条:每次测试都是 45-90 分钟的时间盒,围绕一个”测试宪章”展开。

测试宪章不是测试用例。它更像一个任务声明:“在未来 60 分钟内,将探索商城系统的支付边界,重点关注货币换算和余额不足场景”。

宪章要满足 SMART 原则:具体、可衡量、可实现、相关、有时限。
宪章最重要的记录就是量化工作,以及提醒自己主线任务

我们上一篇也说过游戏中永远不可能100%测试覆盖率,所以需要在有限时间内“聪明”的测试

时间盒也不要求将所有东西都测完,只要求在规定时间内找到足够多的问题。

为什么越测发现的新问题越少

教程给了一个覆盖率增长模型,用微分方程描述:

1
dC/dt = k · (S - C) · exp(-αt)

其中 C 是已覆盖的功能,S 是总功能,k 是学习速率,α 是疲劳系数。

这个公式翻译成人话就是:刚开始测的时候,你对系统不熟,发现新问题的速度很快。随着测试进行,两个东西在变化——剩余未测功能变少了你也累了。那个 exp(-αt) 就是疲劳项,解释了为什么连测两小时后效率断崖式下降。

这引出一个很实际的结论:有预算时间的测试更有效

教程给出的 90 分钟分配方案:

阶段 时长 做什么
准备 5 分钟 阅读宪章、回顾上次测试的笔记
探索 70 分钟 执行测试,每 30 分钟做一次
整理 10 分钟 把发现的 bug 整理成可提交的报告
总结 5 分钟 记录覆盖率变化、标记下一步要测的区域

怎么判断测试是否高效

教程给了一个硬指标:信息密度 D

1
D = (新发现的问题数 + 新覆盖的功能数) / 测试时间(分钟)

优秀测试员的 D 值大于 0.5。也就是说,每两分钟至少要产出一个有价值的发现。
如果两分钟过去了你什么都没发现,说明要么你的测试策略有问题,要么你该换一个宪章了。

两个启发式检查清单

人工测试没法遍历所有情况,但可以用启发式模型帮你系统化地扫描。教程给了两个:

SFDIPOT(面向通用软件):

维度 含义 游戏测试中的例子
Structure 结构 代码模块、资源文件、配置表
Function 功能 攻击、购买、存档、匹配
Data 数据 玩家属性、道具数据、排行榜分数
Interface 界面 UI 布局、HUD、菜单导航
Platform 平台 不同设备、操作系统、屏幕分辨率
Operations 操作 安装、更新、断线重连、后台切换
Time 时间 帧率、延迟、冷却时间、定时事件

GAMEPLAY(面向游戏专有):

维度 含义
Goals 目标:任务、成就、胜利条件
Actions 动作:攻击、移动、交互、使用道具
Mechanics 机制:伤害公式、碰撞检测、AI 行为
Economy 经济:金币、钻石、经验值、掉落率
Progression 进度:等级、技能树、章节推进
Loop 循环:核心循环、奖励周期、每日任务
Aesthetics 审美:画面、音效、UI 风格
Yield 产出:玩家获得什么、反馈是否及时

这两个清单的作用是在不知道该测什么的时候,扫一眼看看有没有维度被忽略了。
是很好用的防遗漏的工具。


二、边界测试:找到那个”刚好会炸”的点

第一章讲了边界条件组合爆炸,这章进入了实战层面边界具体在哪

显式边界 与 隐式边界

显式边界就是文档里写清楚的,整数溢出(2^31 - 1 是 32 位有符号整数的上限,2^63 - 1 对应 64 位)、配置表里标好的等级上限、业务逻辑里定义的 VIP 等级——这些都有明确的数字。

隐式边界游戏开发自己都可能不知道,比如:

  • 渲染管线一次最多绘制多少个对象?超过之后的表现为闪烁还是崩溃?
  • 碰撞检测的精度阈值是多少?两个移动速度极快的物体会不会”穿模”?
  • 网络包大小有没有隐式上限?某些特殊操作组合产生的数据包超限后会怎么处理?
  • 寻路网格的节点数有没有上限?超大开放世界地图会不会让寻路算法超时?
    这些问题,正常流程很难碰到,也让bug复现很困难。

教程给了一个边界测试点的通用集合:

1
B = { a-ε, a, a+ε, (a+b)/2, b-ε, b, b+ε }

其中 a 和 b 是边界值,ε 是一个非常小的偏移量。以等级系统 1-100 级为例,测试点就是 0 级、1 级、99 级、100 级、101 级,再加上中间一个正常值(比如 50 级)。这七个点覆盖了”边界外、刚好边界、边界内、中间值”四种情况。

浮点数的陷阱

游戏里浮点数无处不在:坐标、速度、伤害系数、时间。

一个经典例子:16777216.0f + 1.0f == 16777216.0f

32 位浮点数的尾数只有 23 位,小于 16777216 的时候步长是 1,一旦超过这个值,步长变成 2。也就是说,加 1,结果根本没变。这在伤害计算里意味着什么?攻击力够高的时候,加一点攻击力可能完全没有效果

还有 NaN(不是一个有效数字)和次正规数(极小值区间的精度异常)。这些边缘值在任何计算中出现都会像病毒一样传播,让所有后续结果都变成 NaN。

状态机的三级分层

游戏的状态机比普通软件复杂得多,教程把它分了三级:

1
2
3
4
5
宏观层:菜单 → 游戏中 → 暂停 → 结算

细节层:探索 → 战斗 → 对话 → 商店

原子层:攻击 → 防御 → 闪避 → 技能释放

每一层都是一个独立的状态机,而且它们是嵌套的。玩家可能在”战斗中”的细节层,同时处于”攻击”的原子状态,然后突然接了个电话,App 进入后台(宏观层切换),回来后所有状态需要正确恢复。

测试策略是分四步递进的:

  1. 状态覆盖:保证每个状态都被访问过
  2. 转换覆盖:用转移矩阵 T_ij 记录每两个状态间是否可能出现合法转换,逐一验证
  3. 路径覆盖:挑几条高频路径(比如”登录→匹配→战斗→结算→返回大厅”)做端到端验证
  4. 条件覆盖:同一个转换在不同条件下行为不同(比如”战斗→死亡”,HP=1 时扣 1 点和 HP=100 时扣 100 点结果一样,但中间状态不同)

大多数团队能做到转换覆盖就不错了。

并发状态机的问题更困难。如果有 n 个独立状态机并行运行(战斗状态机、背包状态机、网络状态机……),理论组合数是它们状态数的乘积。教程的建议是用配对测试降维。不测所有组合,只保证每对状态被覆盖至少一次。


三、玩家行为建模

这一节讲怎么测玩家会怎么玩

Bartle 四型

引用了经典的 Bartle 玩家分类:

类型 驱动力 典型行为 测试关注点
成就型 变强、通关 冲排行榜、刷成就 数值上限、排名系统
探索型 发现、理解 找彩蛋、研究机制 隐藏内容、边界交互
社交型 连接、影响 聊天、公会、交易 社交功能压力、消息系统
杀手型 竞争、支配 PvP、捣乱 平衡性、反作弊、恶意行为

每个类型的玩家会以完全不同的方式”使用”游戏。成就型玩家会压着数值边界走,探索型玩家会触发一些从没想过的操作序列,杀手型玩家会主动寻找能伤害其他玩家体验的手段。测试时要为每种类型设计不同的测试宪章。

用信息熵识别机器人

马尔可夫链建模玩家行为,然后计算信息熵来判断操作模式。

假设我们把玩家操作分类(攻击、移动、使用道具、等待),然后统计相邻操作之间的转移概率。正常的玩家操作序列有中等程度的熵:既不是完全随机,也不是完全可预测。

1
信息熵 H(X) = -Σ p(x_i) × log₂(p(x_i))

低熵意味着行为高度规律,转移概率集中,很可能是自动化脚本。高熵意味着每个操作的选择近乎等概率,不像真人——真人不会在”攻击”和”打开背包”之间以 50/50 随机选择。

教程的练习 2.3 给了一个 12 步操作序列的例子:4 种操作分别出现 3/2/1/6 次,计算出熵值约 2.25 bits,明显偏移等概率分布:操作分布高度不均,暗示重复模式,疑似机器人行为。

漏斗分析和断点检测

两种分析玩家群体的方法。

漏斗分析:追踪玩家在每个阶段的留存率。数据可能是:

1
2
3
4
新手教程 100%
→ 首次战斗 85%
→ 首次失败 59.5%
→ 首次付费 35.7%

每一层流失率突然变高的地方,就是测试需要重点关注的区域。如果”首次失败”到”首次付费”的转化率异常低,可能是因为失败后的引导不够,或者付费入口在失败界面没展示好。

断点检测:难度曲线的二阶导数。如果 d²D/dt²(难度变化的加速度)超过阈值 θ,说明难度在某个点突然陡增。这不是”难不难”的问题,而是”难度增长是否平滑”的问题。


四、Bug 复现

Bug 复现是整个测试流程里最考验思维能力的环节。教程把这部分讲得像侦探小说。

Delta 调试:二分法找最小复现集

核心问题是:**”触发这个 bug 的最小操作集合是什么?”**

假设你有一个 20 步的操作序列能稳定复现 bug,但不是每一步都必要。Delta 调试的做法是二分法递归:

  1. 把 20 步对半分,先测前 10 步——能复现吗?能的话问题在前半段,丢弃后 10 步
  2. 把剩下 10 步再对半分,测前 5 步……
  3. 直到找到”去掉任何一步 bug 就不出现”的最小集合

复杂度在 O(n log n) 到 O(n²) 之间,取决于 bug 的”分布特征”。教程练习 2.5 给出了一个 20 步的序列,经过四轮二分,最终锁定最小复现集合为 [H, I, K, L, M] 五步。

因果链追踪

理解bug为什么发生。可以使用因果链模型:

1
触发条件 → 状态变化 → 错误传播 → 可见症状

看到的 bug 是”可见症状”:比如角色突然飞天。
但真正的根因可能在几层之前:某次状态切换时一个标志位没重置,然后这个错误状态被正常逻辑处理了,最后才在渲染层表现为异常坐标。

分层日志在这里是关键工具:

级别 查什么
FATAL 系统崩溃的直接原因
ERROR 异常被捕获的位置
WARNING 被降级处理的边缘情况
INFO 正常流程的节点记录
DEBUG 变量的中间值
TRACE 每一帧的精确状态

追踪 bug 时,从 ERROR 层入手,向下钻到 DEBUG/TRACE 去重建因果链。

确定性重放

“刚才还能复现,现在又不出现了”。
应对这种问题引入一个新概念:游戏状态是初始状态和所有输入的函数

1
S_n = f(S_0, I_1, I_2, ..., I_n)

如果你能记录 S_0(初始状态:关卡、装备、属性值)和所有输入序列 I,理论上就能在任何时候完美重放。很多游戏引擎已经内置了重放系统(回放录像文件本质上就是这个原理),但测试环境往往要求更精细(包括随机种子的记录、网络包的时序等)。

教程列举了 8 个常见的复现陷阱:

  1. 只在特定帧率下触发(帧率变化后消失)
  2. 依赖内存中未初始化的值(重启后消失)
  3. 竞态条件(只在特定线程调度下触发)
  4. 缓存污染(清缓存后消失)
  5. 浮点精度(只在特定硬件上触发)
  6. 时间相关(只在服务器特定时间触发)
  7. 账号状态残留(新建账号不可复现)
  8. 多设备差异(只在特定配置上出现)

每个陷阱对应的调试技巧也不一样:二分法缩小范围、时光机调试(用保存的快照回退到关键帧)、差异对比(能复现和不能复现的环境到底差在哪)、最小化重现、假设验证(先猜一个根因再设计实验验证)。


五、实例分析

练习 2.2:等级系统的边界值分析

练习 2.2 某 RPG 游戏等级系统 1-100 级,经验公式 E(n) = 100n²。请设计边界测试,包括等级边界和经验值边界。

经验公式让边界测试多了一层维度。

等级边界直接用七点法:

1
2
测试点:0, 1, 50, 99, 100, 101
101是溢出边界相当于121 万经验值,刚好到达 100 级的临界点
  • 1 级升 2 级需要 100×(2² - 1²) = 300 经验
  • 99 级升 100 级需要 100×(100² - 99²) = 100×(10000-9801) = 19,900 经验

在 100 级时,经验值继续累加会发生什么?, 溢出不处理(经验条溢出显示)?截断(停在最大值)?回绕(数值超过存储上限变为负数)?

如果是 int32,121 万离 21 亿还很远;但如果是更大规模的经验系统(比如亿万级数值的放置游戏),溢出边界就可能被触及。

0 级。0 级不是正常的玩家状态,但可能在”删号重置”、”GM 指令”、”数据迁移异常”等场景出现。如果经验公式里 E(0) = 0,经验曲线在 0 处的行为需要单独验证。因为在某些公式里 E(0) 可能导致除零错误。

练习 2.3:从操作序列中找出机器人

练习 2.3 一段 12 步操作序列中,4 种操作分别出现 3/2/1/6 次。计算信息熵,判断是否是机器人在操作。

计算过程:

1
2
3
4
5
6
各操作概率:p1 = 3/12, p2 = 2/12, p3 = 1/12, p4 = 6/12

H = -(3/12 × log₂(3/12) + 2/12 × log₂(2/12) + 1/12 × log₂(1/12) + 6/12 × log₂(6/12))
= -(0.25 × (-2.0) + 0.167 × (-2.585) + 0.083 × (-3.585) + 0.5 × (-1.0))
= 0.5 + 0.431 + 0.299 + 0.5
= 1.73 bits

4 种操作等概率分布时,最大熵约 2.0 bits。实际算出的熵偏离这个值,意味着分布不均,某种操作被过度重复疑似自动化脚本

正常玩家的操作频率分布通常接近幂律(少数操作高频,多数操作低频),但不会达到极端程度。出现极端分布,大概率是bot。

练习 2.4:战斗状态机的全覆盖

练习 2.4 某格斗游戏有 6 种战斗状态:待机、攻击、受击、闪避、技能、死亡。设计状态转换测试,需要的测试用例数。

6 个状态,两两之间可能存在转换(包括自身转换,比如”技能→技能”表示连招)。理论转移总数是 6×6 = 36 条,但不是每条都有意义(比如”死亡→攻击”不存在)。

合理的有效转换矩阵大约是:

1
2
3
4
5
6
7
        待机  攻击  受击  闪避  技能  死亡
待机 ✓ ✓ ✓ ✓ ✓ -
攻击 ✓ ✓ ✓ ✓ ✓ ✓
受击 - ✓ ✓ ✓ - ✓
闪避 ✓ ✓ - - ✓ -
技能 ✓ ✓ ✓ ✓ ✓ -
死亡 ✓ - - - - -

粗略统计有效转换约 25-30 条。算上条件分支(攻击衔接窗口、技能冷却中、体力不足等),建议设计 25-30 个核心用例,覆盖:全部状态至少一次 + 全部有效转换 + 3-5 条高频路径的端到端验证。

这个数字看起来不高,但只是原子层。加上细节层(探索/战斗/对话切换)和宏观层(暂停/后台),组合起来就不小了。

练习 2.5:Delta 调试的实战演练

练习 2.5 以下 20 步操作能复现一个 bug,用 Delta 调试找出最小复现子集。

1
A B C D E F G H I J K L M N O P Q R S T

Delta 调试流程:

第 1 轮:测试前 10 步 [A-J]。假设不能复现——问题在后 10 步。

第 2 轮:把后 10 步 [K-T] 再二分,加前 5 步组成 [A-E, K-O]。假设不能复现——问题在 [P-T]。

第 3 轮:测试 [A-E, K-O, P-R] vs [A-E, K-O, S-T]。锁定到 [A-E, K-O, P-R]。

第 4 轮:在有问题的子集里继续收缩。最终锁定最小复现集合——教程给出的是 [H, I, K, L, M]。

不需要理解 bug 的原理就能找到复现集。它纯粹是算法化的排除法,但前提是已经有一个能稳定复现的”大集合”。
如果连稳定复现都做不到,那就得先回到因果链追踪,找到触发条件。

练习 2.8:风险评估矩阵

练习 2.8 对一个包含商城系统的 MMORPG,进行风险评估。要求按”概率 × 影响度”矩阵打分,并提出资源分配建议。

风险评估矩阵:

风险场景 概率 影响度 风险值
支付流程金额错误 极高
道具复制 bug 极高 中高
排行榜数据异常
UI 显示错误(不影响功能) 中低
字体渲染问题 极低
极端并发下服务器崩溃

教程的核心建议是:支付系统(高概率×高影响)应该吃掉 40% 的测试资源。这不是因为它最难测,而是它炸了代价最大。道具复制 bug 虽然概率低,但一旦出现影响是毁灭性的(经济系统崩溃),也应该分配约 20% 的资源做专项测试。

资源分配的最优策略不是”哪里容易出 bug 测哪里”,而是”哪里炸了损失最大测哪里”。这是一种风险驱动的思维。


六、四个方法论提炼

方法论一:时间盒思维

测试永远做不完。时间盒思维要求在有限时间内产出最具决策价值的信息,放弃追求完美覆盖

具体做法:

  • 每次测试前明确”宪章”,知道自己这 45 分钟要回答什么问题
  • 复盘总结时可以用信息密度 D 判断效率,低于 0.5 就调整策略

方法论二:培养对临界点的直觉

可以按下面的问题自查:

  1. 比最小值小 1 会怎样?
  2. 比最大值大 1 会怎样?
  3. 中间有没有隐式边界(浮点精度、数据结构限制、网络包大小)?

方法论三:bug链溯源

看到一个症状,回溯它的因果链

1
2
3
4
5
6
7
看到的现象:角色在空中卡住

直接原因:角色坐标.z 异常为 NaN

中间状态:某次跳跃落地检测返回了 NaN

触发条件:跳跃高度 = 0(在地面跳跃),且地形高度数据缺失

每一步都需要”假设→验证”的循环。

Delta 调试、分层日志、确定性重放,都是这个思维过程的工具。

方法论四:玩家视角

成就型玩家、探索型玩家、杀手型玩家会用完全不同的方式拆解游戏。

具体做法:为每种 Bartle 类型设计独立的测试宪章。测经济系统时,想象你是一个成就型玩家,你会最大化收益、找漏洞、研究最优路线。同一套系统,再想象你是一个杀手型玩家,你会找不给钱的途径、恶意交易、利用经济系统攻击其他玩家。


结语

“人工测试”远不止“点点点”。


下一章:自动化测试——脚本、机器人、CI 流水线中的测试策略。