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存储时同时存了realName和name。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的测试结果是可信的。否则,用户一用就会露馅。