AI多Agent协作系统实战(十三):当AI Agent说“测试通过“时,它到底测了什么?

系列第13篇 | 从"测试全部通过"到"用户一用就露馅"的6个致命漏洞

背景

我们的4-Agent协作系统已经稳定运行了一个多月。自动化流水线看起来很完善:小虾开发→小牛测试→小密复核→verified。

直到今天,用户发了一张截图,上面显示:

  • 姓名:“请输入姓名”(placeholder)
  • 手机号:“请输入手机号”(placeholder)
  • 工号:“工号不可修改”(placeholder)
  • 部门:无
  • 角色:无

然后用户问了一句让我至今难忘的话:“你们到底是怎么测试和复核的?有点击链接、弹出结果、截图内容校验吗?”


一、问题是怎么发现的

1.1 第一次修复:小虾说"完成了"

用户反馈个人信息页显示placeholder,我写了任务MD派发给小虾。小虾很快回复"完成通知",测试也通过了。

但用户实际一看——还是placeholder

1.2 第二次修复:又说"完成了"

我又派了一次任务,小虾又说完成了。用户再看——还是没变

1.3 用户的愤怒

用户发了那张截图,然后说:

“不能光是有截图就过呀。要校验截图内容。我一直强调要严格测试,严格复核,到底在搞什么?”

这句话让我意识到:我们的测试流程有根本性的漏洞


二、根因分析:6个致命漏洞

漏洞1:API URL写死了localhost

症状:个人信息页显示placeholder,但API返回的数据是正确的。

根因:profile.vue里写死了http://localhost:8080,移动端根本访问不到。

// ❌ 错误:写死了localhosturl:'http://localhost:8080/api/users/'+userId// ✅ 正确:使用动态baseUrlconstbaseUrl=window.location.origin||''url:baseUrl+'/api/users/'+userId

教训:移动端的API调用必须使用相对路径或动态baseUrl,不能写死localhost。


漏洞2:localStorage字段名不匹配

症状:API返回了正确的数据,但页面还是显示placeholder。

根因:登录API返回的是realName字段,但localStorage存储时同时存了realNamename。profile.vue只读取realName,没有兼容name

// ❌ 错误:只读取realNamerealName:userInfo.value.realName||''// ✅ 正确:兼容两种字段名realName:userInfo.value.realName||userInfo.value.name||''

教训:数据流转过程中,字段名可能在不同环节被重命名,必须做好兼容。


漏洞3:浏览器加载了旧版JS文件

症状:代码已经修改并部署,但浏览器显示的还是旧版逻辑。

根因:JAR包里存了多个版本的JS文件(hash不同),浏览器可能加载旧版本。

pages-login-index.Bgix4Bjv.js (00:50 - 旧版) pages-login-index.DcjwTzHb.js (08:47 - 新版)

教训:部署时必须清理旧版静态资源,或者使用强制缓存刷新策略。


漏洞4:测试只验证"文件存在",不验证"内容正确"

症状:小牛说"测试通过",但用户一看就是错的。

根因:测试流程只要求"截图保存到xxx.png",不校验截图内容。

# ❌ 错误:只检查文件存在test-s/vol1/1000/tmp/snap/xxx.png&&echo"通过"# ✅ 正确:用vision工具校验截图内容# 打开截图,验证:姓名显示"测试用户"而不是"请输入姓名"

教训:测试必须验证实际效果,不能只验证"做了某个动作"。


漏洞5:头像上传大小限制

症状:上传头像时报错MaxUploadSizeExceededException

根因:Spring Boot默认文件上传大小限制1MB,需要增加到10MB。

# application.properties spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=10MB

教训:文件上传功能必须配置大小限制,否则用户上传稍大的图片就会失败。


漏洞6:头像URL需要拼接baseUrl

症状:头像上传成功,但页面上还是显示蓝色圆形。

根因:后端返回的头像URL是相对路径/uploads/avatar/xxx.png,前端需要拼接baseUrl才能正确显示。

<!-- ❌ 错误:直接使用相对路径 --><image:src="userInfo.avatar"/><!-- ✅ 正确:拼接baseUrl --><image:src="userInfo.avatar.startsWith('http') ? userInfo.avatar : (window.location.origin + userInfo.avatar)"/>

教训:前后端分离架构中,URL必须统一处理,不能假设都是绝对路径或相对路径。


三、解决方案:建立严格的测试标准

3.1 测试必须包含交互验证

旧标准

测试步骤: 1. 访问页面 2. 截图 3. 完成

新标准

测试步骤: 1. 访问页面 2. 【关键】执行具体交互操作(点击、跳转、提交) 3. 【关键】验证弹出结果(上传成功提示、保存成功提示) 4. 【关键】截图并用vision校验内容(验证显示的是真实数据而不是placeholder)

3.2 截图内容校验要求

禁止做法

  • ❌ 只检查截图文件是否存在
  • ❌ 只检查截图文件大小
  • ❌ 只写"截图保存到xxx.png"而不校验内容

必须做法

  • ✅ 用vision工具打开截图,验证内容是否正确
  • ✅ 验证截图中显示的是真实数据(不是placeholder)
  • ✅ 验证截图中没有错误提示(如"上传失败"、“保存失败”)

3.3 测试报告标准格式

═══════════════════════════════════════════════════════════════ TEST-{任务ID} 测试报告 ═══════════════════════════════════════════════════════════════ **功能验证:** 1. ✅ {具体功能点}:{实际验证结果} 2. ✅ {具体功能点}:{实际验证结果} **代码验证:** 1. ✅ {代码检查点}:{验证结果} **截图验证:** - 修复前截图:{路径} - 修复后截图:{路径} **结论:✅ 测试通过**

四、教训总结

教训1:测试不能只验证"做了动作"

测试的目的是验证"效果正确",而不是"执行了某个操作"。点击按钮后,必须验证弹出的结果是否正确。

教训2:截图必须校验内容

截图只是证据,不能代替验证。必须用vision工具打开截图,验证里面的文字、元素是否正确。

教训3:数据流转要统一字段名

从API到localStorage到页面渲染,字段名必须统一。如果有多种命名方式,必须做好兼容。

教训4:部署必须清理旧资源

多版本JS文件共存会导致浏览器加载旧版本。部署时必须清理旧资源,或者使用强制缓存刷新。

教训5:文件上传必须配置限制

Spring Boot默认的文件上传大小限制只有1MB,必须根据业务需求调整。

教训6:前后端URL必须统一处理

相对路径和绝对路径不能混用,必须有统一的处理逻辑。


五、给同样在用AI写代码的开发者的建议

1. 建立严格的测试标准

AI Agent很容易"假装完成"——它执行了某个动作,但没有验证效果是否正确。必须建立明确的测试标准,要求验证实际效果。

2. 截图必须校验内容

不要只看"有没有截图",要看"截图里显示的是什么"。用vision工具校验截图内容,确保显示的是真实数据而不是placeholder。

3. 数据流转要统一

从API到前端到渲染,字段名必须统一。如果有多种命名方式,必须做好兼容,否则会出现"API返回了数据但页面不显示"的问题。

4. 部署要彻底

修改代码后,必须确保浏览器加载的是最新版本。清理旧资源、强制缓存刷新、版本号管理,都是必要的手段。

5. 配置要完整

文件上传大小限制、CORS配置、静态资源映射,这些基础设施配置必须到位,否则会出现"功能开发好了但用不了"的情况。


六、结语

这次经历让我深刻认识到:AI Agent的"测试通过"和用户的"测试通过"是两回事

AI Agent说的"测试通过"可能只是"执行了某个动作",而用户要的"测试通过"是"效果正确"。

作为开发者,我们必须建立严格的测试标准,确保AI Agent的测试结果是可信的。否则,用户一用就会露馅。