Skip to content

测试替身比真实实现宽松:四个缺陷因此带着绿灯发布——需要一条把替身钉在真实契约上的闸门 #4550

Description

@os-zhuang

v17 验收批次(#4482)修完后回看,四个缺陷共享同一个根因,而且这个根因本身没有被修:测试替身(假引擎、夹具、桩件)比它替代的真实实现更宽松,于是缺陷在既有测试全绿的情况下发布。

这不是四次巧合。它是一类可复现的失效模式,且当前没有任何闸门看得见它。

四个实例

缺陷 替身 它比真实实现宽松在哪
#4434 plugin-sharing 测试里的假引擎 deleteRule 用谓词删 sys_record_share,既无标量 id 也无 multi:true——正是真实 engine dispatch 唯一拒绝的形状。既有测试 deleteRule drops rule + all its grants 一直在对着一个真服务器必然 500 的调用断言成功。
#4441 dogfood field-zoo 夹具 注释直言 // FK enforcement is off in this harness, so this asserts value fidelity,并往 lookupacc_synthetic_0001 等不存在的 id——夹具依赖着平台正要禁止的那个缺陷
objectui#3134 设计器标签解析无对照
objectui#3129 ObjectTimeline.test.tsx./renderer 换成只渲染 item.title 的桩件 分桶结果只存在于真实渲染器输出的表头里,于是「uses spec-compliant startDateField」这条用例无论日期绑定解析成功与否都通过。同文件 ListView.test.tsx 只断言切换器里出现 Timeline 选项,从不检查转发下去的绑定。

另有一条同形态的问题在 #4433 修复中暴露:rule-rebind.test.tsskips system-context writes 用例把缺陷行为本身钉成了断言(用的是真实路径根本不会发送的 mock session)。

为什么这类问题格外贵

三重代价:

  1. 它让绿灯失去意义。 缺陷发布时门禁是绿的,而且绿得理直气壮——测试确实跑了、确实通过了。
  2. 它自我复制。 写新测试的人(尤其是 agent)会模仿既有测试的 mock 方式。一个宽松的假引擎会长出第二个、第三个。
  3. 它专挑最需要覆盖的路径。 替身之所以被引入,正是因为真实实现在那里「不好测」——而不好测的地方往往就是契约最密的地方。

已有的两处正确做法,可作为方向

本批次的修复里已经出现了对的形状,可以推广而非从零设计:

建议方向(未实现,待讨论)

不是要禁用测试替身——那不现实。目标是让「替身与真实实现的契约差异」可见且会报警

  1. 给关键替身加契约钉:假引擎/假 driver 的拒绝面必须与真实实现的守卫对齐,并有测试证明「放松替身 → 某条既有断言变红」。sharing: DELETE /sharing/rules/:idOrName answers 500 for both address forms — rules cannot be deleted over REST #4434 的修复已经演示了这个形状。
  2. 禁止在断言目标本身上使用桩件:objectui#3129 的教训——断言分桶结果就不能把渲染器换掉。可考虑一条 lint 规则或评审清单项。
  3. 夹具中「关闭平台约束」的注释视为欠债标记:像 FK enforcement is off in this harness 这种,应当进入台账并被要求给出理由与到期条件,而不是长期沉默存在。

第 3 条与仓库已有的 UNWIRED_RULE_LEDGER#4449)、NO_DATA_REACH(objectui#3144)是同一个模式:已知的不完整必须被声明,而不是被默认

范围

本 issue 只记录模式与方向,未认领。真正的闸门形态需要设计讨论——尤其第 1 条如何在不同包的替身之间通用,可能需要一个共享的「契约一致性」测试工具而非逐包手写。

发现自 #4482 v17 验收批次。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions