基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
  • 知识卡片碎片时间过考点
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
    • AI 热点每日 AI 资讯与动向
AI 工作台
简历
旧版
AIHT
内容
精选全部 AI 动态热点榜AI 日报收藏
分类
模型行业编程产品技术观点开源
更多
Agent 接入
返回首页开发者导航
AIHT
精选 / 资讯详情
8.0
热点
AI SCORE
编程提效2026-09-21 09:54

用破坏不变量法审查 AI 生成代码

dev.to · AI#代码审查#AI编程#测试
Editor brief · 编辑速览

通过故意破坏代码不变量来验证测试有效性,可发现 AI 生成代码中测试未覆盖的逻辑漏洞。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

AI 助手返回了一个 diff 和配套的测试,所有测试都通过了。尴尬的时刻在于,你仍然无法用自己的话解释这些测试究竟在保护什么。

一种审查技巧是选择一个不变量,故意打破它,然后检查是否有测试失败。失败的预期会指向它在实现中保护的条件。

本文将 SquadNote 真实的通知路由器及现有测试提取到一个临时副本中。删除一个删除条件后,8 个通过的测试变成 7 个通过加 1 个失败。检查于 2026 年 9 月 11 日执行,使用 Node.js v24.15.0 和 Vitest 4.1.4。

这个实验的目的不是判断代码的作者身份。它展示的审查流程同样适用于从 AI 助手那里收到的代码。这里描述的是已验证过的流程,不声称持续使用会带来什么好处。

将"登出正常"转化为关于存储数据的条件

示例涉及删除设备的推送令牌。假设一个设备从账户 A 切换到账户 B,随后收到来自 A 的延迟注销请求。

"登出正常"这种表述留给了太多解读空间。这里的不变量是:

来自前所有者 A 的请求不得删除已转移到 B 的设备令牌。

这明确了执行者、目标和必须保留的数据。相比"正确处理授权",它更容易转化为测试。

相关的删除谓词是:

and(
  eq(pushTokens.userId, ctx.session.user.id),
  eq(pushTokens.token, input.token),
)

令牌必须匹配,且其当前所有者必须与会话用户相匹配。注册和所有权转移是一个更大的话题;本次审查聚焦于删除谓词。

在修改代码前先写下反例

从操作序列开始:

A 注册了 ios-1 和 android-1。

B 注册了 android-1,将该令牌的所有权转移给 B。

A 请求注销 android-1。

B 的 android-1 和 A 的 ios-1 必须保留。

现有测试已经遵循了这个序列。它在之后读取数据库行,而不是仅检查是否调用了 API。其最终预期是:

expect(await rows()).toEqual([
  { user_id: "user-b", token: "android-1", platform: "android" },
  { user_id: "user-a", token: "ios-1", platform: "ios" },
]);

rows() 从哪里读取也很重要。这些测试使用真实的路由器和 libSQL 测试数据库,外部 HTTP 被 mock 了。如果 unregisterPushToken 本身被替换为一个总是成功的 mock,这个测试就不会执行这个谓词。

仅删除用户条件

实验将提交 0cda1e8 提取到临时目录并运行了原始测试。然后仅在该副本中将谓词改为:

eq(pushTokens.token, input.token)

这是一个为了检验测试而注入的故障。这不是提议的生产变更。仅按令牌删除应该允许 A 的过期请求移除 B 的注册。

失败的测试是那个断言切换账户只转移该设备且前所有者无法删除它的测试。它对存储行的预期不再匹配。

一个已经失败的测试无法确立这种差异。一个阻止测试套件启动的语法错误也无法确立预期的不变量被检测到。这里,失败发生在测试的数据预期上。

实验脚本提取修复的提交,运行基准,移除条件,再次运行测试,且不修改原始仓库。假设源项目的依赖已安装,从文章仓库运行:

node experiments/article-stock-2026-09/mutation-check.mjs ../circle-hub-multi-device-push

如果测试仍然通过,需要检查什么

这个反例被检测到了。如果不同的变更导致测试套件仍然通过,先检查反例是否到达了目标代码,再考虑增加更多测试。

例如,一个总是提供用户 A 的 mock 无法建立到 B 的转移。仅仅检查成功响应而不在之后读取行的断言可能会漏掉过度删除。测试也可能执行了与你修改的路由器不同的路由器。

按顺序阅读输入设置、调用的函数、外部边界的 mock 和最终预期。一旦你确定了哪些组件实际运行,就添加缺失的反例。

这个流程确定了一个选定的测试能够检测到一个选定的故障。它不能证明不存在每个授权 bug、每个有问题的并发顺序,或对真实设备的投递失败。这个实验没有使用外部投递或物理设备。

当向 AI 助手请求解释时,"如果移除这个条件,哪些数据会消失,哪个预期会失败?"能产生一个可以与执行结果对比的答案。记录不变量、反例和实际失败的预期。这份记录帮助下一个审查者判断相同的条件是否可以在清理期间被移除。

对于进一步的操作,你可以考虑屏蔽此人或举报滥用。

Original source

本文由 AI 翻译整理,来源 dev.to · AI,原文版权归原作者所有。

阅读原文
上一篇
用Responses API构建有边界的GPT-6 Astra Agent
下一篇
AI 编程测试冻结:保存异常指纹而非测试名

资讯速览

dev.to · AI · 2026-09-21 09:54

AI 评分 8.0 / 10

相关标签

#代码审查#AI编程#测试

继续阅读

  • 阶跃Step 5 Preview实测:27B参数开源模型冲至Top2
  • 用Responses API构建有边界的GPT-6 Astra Agent
  • AI 编程测试冻结:保存异常指纹而非测试名
  • 程序员亲历:全公司用 Claude Code 批量生产代码,没人读代码,12 小时工作制
  • 自托管Llama部署成本全解析:GPU之外容易被忽视的隐性开销