Files
gplan-2/design/engineering/test-conventions.md
T
DKChan feaf01454a docs(验收闭合): 测试规范新增 §9 改动级验收闭合——每个需求点与行为改动都要真实入口断言+撤销证明,合回前独立验收审计
病根=测试规范 §5.2 只要求「每条清单条目至少一个自动断言」,没要求断言从改动的真实入口进入:
裸 EffectEngine 断言可替 BattleOrchestrator 接线,报告写「V1 绿」而 V1 断言并不存在。

- test-conventions §9(9.1–9.8):验收单位=行为改动;四层与真实入口;改动—验收对照表六列
  双端对上(需求点正向、diff 反向、断言挂载测试侧);撤销证明(改既有行为回退变红、新增行为
  变异变红);禁削弱测试;明确不要求 TDD;合回前独立验收审计;旧卡不回头改、开工前补齐
- process-conventions §3:第 7 条挂对照表、第 9 条补撤销证明、第 10 条合回前审计、新增第 16 条
- AGENTS.md 硬性红线新增「验收闭合」,与变异验证并列
- pipeline_gate 新增 C3:卡片须有对照表节、五个必需列、至少一行、必填格非空、全文声明验收审计;
  只查结构不判语义(生效 2026-10-21,按卡片文件名日期追新)
- reports/README 与 batch-closure 同步验收审计报告的落点与退役判据
- 待办清单登记「验收闭合规则——下一次迭代实测评估」,按规则评估红线等真实迭代检验
2026-10-21 03:42:37 +08:00

13 KiB
Raw Blame History

测试规范(Dungeon Sweeper)

约束范围:game/smoke/、game/tests/ 与一切场景探针的编写与验收。 分工:测试执行许可(什么时候允许跑测试)归 AGENTS.md「硬性红线·测试执行两入口铁规」;跑什么闸、什么顺序归 tools/run_all_gates.sh(脚本即清单,本文与 AGENTS.md 均不另列);本文管测试怎么写、怎么算合格。

写任何测试前必须通读本文。


1. 测试总纲——"存在"必须证明"可达且正确"

核心层验收标准通过不算有测试:GDScript 编译不查字典字段与时序,呈现层错误只在运行时暴露。 测试必须证明功能可达(用户真的能用到)且行为正确(结果符合验收标准)。

2. 场景级探针标配

每新增一个可交互 UI 区域(视图/覆盖层/面板),必须配套场景级 headless 探针: 真实实例化场景 → 驱动真实输入路径 → 断言可见性与状态。

凡模态覆盖层,探针必须断言:打开后自身 visible 且遮挡物已隐藏(模态被遮挡 = 功能存在但不可达)。

3. 缺陷修复必须沉淀回归断言

每个缺陷修复必须在探针或冒烟中留下能抓住该缺陷的断言(例:封印动作耗尽 AP → 必须进入第 2 轮; 选用道具 → 库存态必须变 Used)。没有回归断言的修复视为未完成。

4. 提交前全量验证

交付前跑 tools/run_all_gates.sh(不带 --quick)全绿——该脚本是全部验证闸的唯一入口与唯一清单,本文禁另列闸件清单。闸件增减只改脚本。 行为探针使用随机化玩家行为(封印/停手/刷新/无效点击)——自动玩家不做的动作就是测试盲区。

5. 测试函数怎么写——验收清单条目的落码规范

需求卡片 §8「改动—验收对照表」每一条都要落成可执行的验证;本文定统一写法,卡里定每批的具体内容(测什么/注入哪里)。写卡期查不出执行载体的条目=不可测,当场呈报,禁拖到实现期。

  1. 执行载体五选一,逐条声明:单元测试(tests/run_all)/冒烟断言(smoke_all.gd 对应场景段)/场景探针(smoke/ 下 *_probe)/MC 同种子逐项比对/人工核对(仅限无法自动化的纪律类需求点,如命名锁定、数值占位)。行为类条目以自动化载体为主。

  2. 粒度与对应关系:每条清单条目至少一个自动断言,且断言必须从该条目的真实入口进入;多条条目可合用一个测试函数,但撤销对应改动时每条都要能单独变红。断言失败消息末尾带编号尾缀〔卡短名 Vn〕(如〔秘宝批 V3〕)——断言与卡片清单可互相 grep 查找。

  3. 断言词汇按验证对象五选一:

    词汇 适用场景
    逐位相等 同种子下确定性公式与结算的精确逐项比对
    区间/差值带 概率与平衡读数(MC 同种子配对比较,出带即红)
    快照比对 存档域结构、配置表行结构
    重放 同种子断点回放、改前改后配对
    状态/计数器断言 条件解锁、限次、充能——置数据→走路径→断计数值
  4. 调试注入两层(能力测试与流程体验两分法的落码面):自动化测试=测试代码直调核心层写口构造状态(直改数据表行/计数器/存档域,不经 UI、不走正常资源积累流程——冒烟现役做法);人工体验=DebugOverlay/DS_DEBUG 调试面,服务用户目检。清单条目的「怎么测」必须写到数据面粒度:改哪张表哪行、置哪个计数、调哪个核心写口。

  5. 换代批旧验证约束同生死:删除旧数据行/旧实现代码的批次,必须同批摘除只验证旧实现的断言与护栏校验,由新断言替代——旧件断言残留=冒烟假绿来源;合并后冒烟全绿且全仓无旧件残留引用为证。

6. 变异验证——验收函数的拦截能力自证

测试绿只证明「当前代码没犯这个错」,不证明「犯了这个错会被抓住」。新增或改写的验收函数(冒烟断言/探针自检/闸机判据)必须配一次变异验证,把拦截能力自证一遍。无变异红记录的断言不写「已验收」,只写「已实现,拦截能力未证」。

行为改动的撤销证明见 §9.4。新增或改写断言用变异验证;修改既有行为用撤销证明。两者不互相代替:变异证明断言能抓这一类缺陷,撤销证明断言真的钉在这次改动上。

操作四步(逐条落实现报告变异验证记录表):

  1. 注入——把被测件改坏成该类缺陷的最小形态(翻装配序、注释一行 add_child、把状态源取反、把层 visible 置 false)。一次只注入一处;可直接改源码,不必留长期开关。
  2. 跑最窄的那一道闸——只跑能覆盖该断言的最小单元(单冒烟脚本/单探针/单比对命令),禁跑全套闸(见 process-conventions §6 闸机轮次纪律)。判据:注入后该断言必须变红;红在别处而目标断言仍绿=判据没打在缺陷上,按第 4 条处理。
  3. 精确还原——git checkout -- <文件>(不用 git stash、不手工回改,防残留)。还原后复跑同一道闸确认回绿,才算本轮闭环。
  4. 记录——变异点(文件:行)、跑的闸、红输出摘要、还原结论,逐条进实现报告记录表。

变异不红怎么办(二选一,不许含糊带过):

  • 改判据——把断言打到真正的缺陷面上(例:断言「菜单标题可见」只查截图墨色占比时,把菜单层藏起来仍不红——底层棋盘透过标题框照样给出高反差像素;改查 is_visible_in_tree 或加 golden 逐像素比对才红)。
  • 如实标注拦截边界——判据本身只覆盖弱面的(如「只证伪整框空白/整框糊,不证明框里是何内容」),在断言注释与报告里写明边界,并说明强判据由哪一件承担。

判据强弱分层:同一缺陷面往往需要多件配合——可见性/状态源类缺陷由 is_visible_in_tree、树序、状态源取值的运行期断言抓;内容/观感类缺陷由 golden 逐像素比对抓;占比/统计类判据(墨色占比、纹理幅度)只能兜「整框空白/整框糊/压 busy 纹理」,是补充不是主力。写卡时按此分层配齐,别指望一条断言包打。

7. 判定三硬闸——闸承载的构造义务(迭代 29 起)

任何新增或改写的闸承载(闸脚本/批跑编排/探针入口)必须三闸齐备;现行各闸状态登记归架构文档「测试与质量域」节(本文不另列闸件清单):

  1. 退出码为唯一权威判据——输出里的 PASS 串只作附加判据不作判据主体;打印 PASS 后 quit(1) 必须判红。
  2. SCRIPT ERROR / Parse Error 扫描面在位——GDScript 脚本错误不改进程退出码、不计失败数(机制事实见 §8),错误标记必须扫输出,且判据必须覆盖「跑了但半途脚本错误」态——退出码 0 但输出缺预期签名同样判红(防静默退出)。
  3. 断言数下限——零断言的正常返回与断言失败必须区分开(现行做法:Kit.finish 零断言返回专用退出码 2,不与「断言失败=1」混用,否则致命类套件的期望码协议会放过空跑)。
    • 负面套件(测试目标就是产生错误输出)仅可豁免第 2 闸,且须在 manifest 显式标记,其余两闸照判。
    • 子进程输出须经系统命令解释器重定向落盘再读——OS.execute 的输出管道在本机恒空。

新写测试件全类型标注——新写的测试件(冒烟断言/探针/单测)参数、返回值、容器一律带类型标注,与 S9 对产品码的要求同型;存量不强制清扫,被批次触碰时顺带换代。暂不机器闸(S9 扫描面=产品码,smoke/tests 豁免是测量面直构惯例的既有裁量)。

8. 判定可靠性优先于覆盖率(迭代 29 核心命题)

  • 「跑一遍没崩=通过」无效:测试绿只证明当前代码没犯这个错,不证明犯了这个错会被抓住;断言存在不等于断言执行。
  • 脚本错误会静默中断所在函数的剩余断言,而套件仍报全绿(机制事实):GDScript 无脚本错误陷阱 API,错误不改进程退出码、不计失败数。三处实证:①2026-09-19「309 项 0 失败」实为断言未跑全(状态记录 83);②敌人表行集纪律 10 条断言因定型数组赋值错误从未执行(迭代 29 修复后两条正向见红=夹具本缺锚行);③Callable 形参类型写错致解析函数返回 null、后续断言整段蒸发。
  • 由此产生两条义务:写闸件按 §7 配三硬闸;写验收函数必须自证断言全数执行——变异验证(§6)为主证,反例实证(构造缺陷态改前判绿/改后判红)与零命中自检(扫描面为空即判红)为辅证。
  • 覆盖率不是目标:行覆盖率度量缺失(Godot 无内建,第三方属新基建,是否引入待办清单在册待裁);现行补洞=零指名单测清单+人工判定,判定可靠性靠上列自证手段,不靠覆盖率数字。

9. 改动级验收闭合(2026-10-21 立)

9.1 验收单位是行为改动

一个行为改动 = 新增或修改一条函数、一条数据、一处接线、一个生命周期行为、一条玩家可见文案。纯改名、注释、格式化不算行为改动,但必须在对照表里单独列一行写「非行为改动」及判断依据。禁止用「重构」两个字把行为改动藏起来。

9.2 四层与真实入口

  • 单元:不依赖场景树和真实宿主的纯逻辑;测试直接调用被测函数。
  • 接口:模块边界的公开契约——数据表加载、存档与断点、事件载荷、命令接口;测试从公开入口进出。
  • 集成:跨宿主的接线、持久、生命周期——例如 LevelRunner 把 level_counters 传给 BattleOrchestrator、两场战斗共享同一个桶、断点续玩;测试必须实例化真实宿主链,禁止用裸 EffectEngine 代替。
  • 探针/golden:玩家可达性与观感;真实实例化场景、驱动真实输入。

分层不是形式。改动发生在哪一层,验收就必须从那一层的真实入口进入。测试绕过了改动点,就不算这个改动的验收——这正是「断言存在、接线坏了却不红」的来源。

9.3 改动—验收对照表

需求卡片列需求侧对照表;实现报告列实际对照表。每行至少六格:

需求点 改动(文件:行/行为) 验收测试(名称+文件:行+尾缀) 层级 真实入口 撤销或变异证明

实现报告另加「结果」格(绿/红/未过及依据)。两端都要对上:

  • 正向:每个需求点至少一行;每行必须有真实入口和证明方式。缺任一项=该需求点未验收。
  • 反向:diff 里每个行为改动至少一行;没有对应行的改动,必须写明「非行为改动」或「由第 X 行覆盖」。
  • 测试侧:新增或改写的每条断言至少挂一行;挂不上的孤儿断言删掉或补挂到需求点。

9.4 撤销证明

  • 修改既有行为:只撤销该改动(回退该 hunk 或该处接线)→ 跑能覆盖它的最窄测试 → 对应断言必须变红 → 还原 → 复绿。
  • 新增行为:用 §6 的变异办法把新代码改坏成最小缺陷形态 → 对应断言必须变红。
  • 撤销后仍然绿:说明断言没钉在这个改动上,判缺漏;不得用「代码能跑通」代替。
  • 每张任务卡至少一条撤销或变异记录;持久化、生命周期、存档、跨战共享这类接线逐行做。

9.5 禁止削弱测试

实现期不得为了让测试变绿而删除断言、放宽阈值、缩小扫描面、把真实入口换成下层直调。确需改判据=需求卡片没写清=上游缺陷:停该卡、呈报退回写卡段,不在实现期自行决定。卡片可预先声明允许改动的旧断言及理由;未声明的削弱由独立审计判红。

9.6 测试时机

不要求 TDD,不要求先红后绿。允许实现后补测试;但补的测试必须通过 §9.4 的撤销证明。实现完再补、补完就绿,不算验收。

9.7 独立验收审计

合并前由没有参与实现的会话或子代理做一次审计。审计只读需求卡片、实际对照表、diff、测试文件和实现报告,自己跑最窄测试抽查,不采信报告里的「已全绿」。审计至少做三件事:

  1. 逐行核对对照表:测试存在、真的执行、尾缀能 grep 到、真实入口没有绕过改动点;
  2. 抽查撤销证明:每张任务卡至少一条,按记录复现红/绿;
  3. 检查测试削弱:删除断言、放宽阈值、缩小扫描面、换下层直调,逐处要卡片申报或用户批准。

审计报告落 reports/iter<N>-验收审计-<日期>.md,逐行写「通过/缺漏/例外及依据」。未通过不得合并。测试执行两入口铁规不变:审计在迭代 loop 内启动的子代理算 loop;另开会话须用户点名。

9.8 生效与旧卡

本规自 2026-10-21 起对文件名日期不早于该日的需求卡片生效;旧卡不回头改。旧卡在开工前缺对照表的,由写卡段补,补完再开工。