Human Verify - 模拟人类多角度验证
在完成代码编写后,通过完全不同的视角进行验证,不依赖传统的单元测试、编译检查或静态扫描。
核心理念
人类审查代码时,不只是看代码能不能跑,而是会问:
- •"如果我是用户,这样操作会发生什么?"
- •"如果数据不是预期的格式呢?"
- •"这个功能的边界在哪里?"
- •"逻辑上说得通吗?"
触发条件
- •用户说 "/verify"、"验证一下"、"帮我检查"
- •刚完成一段复杂的代码编写
- •涉及关键业务逻辑的修改
- •重构后需要确认行为一致性
验证策略矩阵
根据代码变更类型,选择合适的验证组合:
| 变更类型 | 推荐验证方式 |
|---|---|
| API 端点 | 逆向验证 + API 契约验证 + 用户场景模拟 |
| UI 组件 | 用户场景模拟 + 状态机验证 + 边界测试 |
| 数据处理 | 数据流追踪 + 边界测试 + 逆向验证 |
| 业务逻辑 | 双向推导 + 用户场景模拟 + 逻辑一致性 |
| 重构代码 | 行为对比 + 回归场景 + 数据流追踪 |
| 配置变更 | 环境矩阵验证 + 边界测试 |
验证方法详解
1. 逆向验证(Reverse Verification)
思路: 从预期结果反推,代码是否能产生这个结果?
步骤:
- •明确期望的输出/行为
- •手动追踪代码路径,验证是否能达到
- •检查是否有遗漏的分支会导致不同结果
示例场景:
code
期望: 用户提交表单后,数据应保存到数据库并返回成功消息 逆向追踪: - 返回成功消息的代码在哪? → SaveAsync() 返回后 - SaveAsync() 真的会被调用吗? → 需要通过 validation - validation 会阻止正常数据吗? → 检查验证规则
2. 边界条件测试(Boundary Testing)
思路: 人类测试员会故意输入"刁难"的数据
检查清单:
- • 空值 / null / undefined
- • 空字符串 vs null
- • 空数组 vs null vs 单元素数组
- • 数字: 0, -1, MAX_INT, 小数, NaN
- • 字符串: 超长, 特殊字符, emoji, 换行符
- • 日期: 时区边界, 闰年, 1970-01-01
- • 并发: 同时两个请求修改同一数据
执行方式:
bash
# 对于 API,构造边界请求
curl -X POST http://localhost:5000/api/xxx \
-H "Content-Type: application/json" \
-d '{"name": "", "count": -1}'
# 对于前端,检查组件对边界 props 的处理
# 在代码中查找:props 是否有默认值?是否有类型检查?
3. 用户场景模拟(User Scenario Simulation)
思路: 假装自己是一个真实用户,按正常流程操作
步骤:
- •列出 3-5 个典型用户场景
- •对每个场景,逐步追踪代码执行路径
- •检查用户在每一步会看到什么
场景模板:
markdown
## 场景: [场景名称] **用户角色**: [普通用户/管理员/新用户] **前置条件**: [用户状态、数据状态] ### 操作步骤 1. 用户执行 [操作A] - 代码路径: [函数调用链] - 预期结果: [用户看到什么] - ✅/❌ 验证结果 2. 用户执行 [操作B] ...
4. 数据流追踪(Data Flow Tracing)
思路: 追踪一个数据从输入到输出的完整路径
步骤:
- •选择一个关键数据字段
- •从入口开始,追踪它经过的每个函数
- •检查每一步的转换是否正确
追踪模板:
code
数据字段: userId
入口 → Controller.CreateSession(request)
↓ request.UserId (string)
处理 → SessionService.Create(userId)
↓ 参数传递正确?类型一致?
存储 → _repository.Insert(session)
↓ session.UserId 赋值正确?
输出 → Response { SessionId, UserId }
↓ 返回的 UserId 与输入一致?
5. 双向推导(Bidirectional Reasoning)
思路: 分别从"代码做了什么"和"需求是什么"两边推导,看是否相遇
步骤:
- •正向: 阅读代码,总结它实际做了什么
- •反向: 阅读需求/注释,理解它应该做什么
- •对比: 两者是否一致?有遗漏?有多余?
输出格式:
markdown
## 双向推导结果 ### 代码实际行为 - 接收参数 A, B, C - 验证 A 不为空 - 调用服务处理 B - 返回结果 D ### 需求期望行为 - 接收参数 A, B, C - 验证 A, B 都不为空 ← ⚠️ 代码遗漏了 B 的验证 - 调用服务处理 B - 记录审计日志 ← ⚠️ 代码遗漏了审计日志 - 返回结果 D ### 差异 1. [差异1] 2. [差异2]
6. API 契约验证(API Contract Verification)
思路: 验证 API 的输入输出是否符合约定
检查项:
- • 请求参数是否都有验证?
- • 必填字段是否标记 [Required]?
- • 响应格式是否与文档/类型定义一致?
- • 错误响应是否有正确的状态码和消息?
- • 是否处理了所有可能的异常?
执行方式:
bash
# 1. 读取 Controller 代码,列出所有参数 # 2. 检查 Request DTO 的验证属性 # 3. 检查返回类型与 Response DTO 是否匹配 # 4. 构造错误请求,验证错误处理
7. 状态机验证(State Machine Verification)
思路: 如果涉及状态变化,验证状态转换是否完整
步骤:
- •列出所有可能的状态
- •绘制状态转换图
- •检查每个转换是否有对应代码
- •检查是否有非法转换被阻止
状态图模板:
code
[Created] --submit--> [Pending] [Pending] --approve--> [Approved] [Pending] --reject--> [Rejected] [Approved] --cancel--> [Cancelled] ⚠️ 检查点: - [Created] 能直接到 [Approved] 吗?(不应该) - [Rejected] 能重新提交吗?(需要确认需求)
8. 行为对比验证(Behavior Comparison)
适用: 重构场景
步骤:
- •记录重构前的行为(通过阅读旧代码或 git history)
- •记录重构后的行为
- •逐项对比,确认行为一致
对比模板:
markdown
| 场景 | 重构前 | 重构后 | 一致? | |-----|-------|-------|--------| | 正常输入 | 返回 A | 返回 A | ✅ | | 空输入 | 返回 null | 抛异常 | ❌ | | 并发请求 | 最后写入胜 | 最后写入胜 | ✅ |
执行流程
Step 1: 确定验证范围
bash
# 查看本次改动涉及的文件 git diff --name-only HEAD~1 # 或者查看工作区的改动 git diff --name-only
Step 2: 选择验证策略
根据变更类型,从策略矩阵中选择 2-3 种验证方式组合使用。
Step 3: 执行验证
对每种选中的验证方式:
- •按照该方式的步骤执行
- •记录发现的问题
- •给出验证结论
Step 4: 输出验证报告
markdown
# 验证报告 ## 验证对象 - 文件: [文件列表] - 变更类型: [API/UI/逻辑/重构] ## 采用的验证方式 1. [方式1] - 原因 2. [方式2] - 原因 ## 验证过程 ### [方式1名称] [详细验证过程和发现] ### [方式2名称] [详细验证过程和发现] ## 发现的问题 1. [问题1] - 严重程度: 高/中/低 2. [问题2] - 严重程度: 高/中/低 ## 结论 - ✅ 验证通过,可以提交 - ⚠️ 验证通过,但有以下建议: ... - ❌ 验证未通过,需要修复: ...
实际执行示例
示例: 验证一个新的 API 端点
假设刚完成 POST /api/sessions 端点:
markdown
# 验证报告: POST /api/sessions
## 采用的验证方式
1. 逆向验证 - 验证核心流程
2. 边界测试 - API 输入验证
3. 用户场景模拟 - 真实使用场景
## 验证过程
### 1. 逆向验证
期望: 创建会话并返回会话ID
追踪路径:
- SessionsController.Create() ✅ 入口存在
- _sessionService.CreateAsync() ✅ 服务调用
- _repository.InsertAsync() ✅ 数据持久化
- return Ok(new { SessionId = session.Id }) ✅ 正确返回
结论: 核心流程完整 ✅
### 2. 边界测试
- 空 projectId: 返回 400 ✅
- 不存在的 projectId: 返回 404 ✅
- 重复创建: 允许(符合需求)✅
- 超长 title: 验证限制 ✅ (MaxLength 200)
### 3. 用户场景模拟
场景: 用户首次创建会话
1. 选择项目 → 代码正确获取 projectId ✅
2. 输入标题 → 验证通过 ✅
3. 点击创建 → 返回会话,跳转成功 ✅
## 发现的问题
1. 缺少 title 为空时的友好错误消息 - 中
2. 没有记录创建者 userId - 需确认是否需要
## 结论
⚠️ 验证通过,建议添加友好错误消息
注意事项
- •不要重复已有验证: 如果已经有单元测试覆盖,就不需要重复验证相同的点
- •关注人类视角: 验证时要像人类一样思考,不是机械执行
- •记录思考过程: 验证的价值在于发现问题,要记录"为什么这样想"
- •适度验证: 不是每次都要用所有方式,根据变更复杂度选择
- •双向验证: 对于复杂逻辑,一定要从两个不同角度验证