You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
haoliang-net/docs/06-测试规范.md

189 lines
9.3 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 测试规范
**版本:** 2026-05-03
**适用范围:** CNC机床数据采集系统 — 前端管理后台 + 后端API
---
## 一、6条铁律
| # | 铁律 | 要求 |
|---|------|------|
| 1 | **接口先验证** | 改了任何API立即用PowerShell调用逐字段确认返回值与前端绑定一致 |
| 2 | **数据断言必须具体** | 禁止只检查 `toBeVisible()`,必须检查文本内容,排除空/`-`/`--`/`undefined`/`null` |
| 3 | **下拉框必须展开验证** | 展开→选项≥1→选择→确认显示值→点查询→确认筛选生效 |
| 4 | **表格必须检查每列** | 行数>0 + 第一行每列文本非空(日期、名称、数值、时间、状态逐列检查) |
| 5 | **卡片数值必须非空** | 每个卡片数值 ≠ 空 ≠ `-``--``undefined``null`允许为0时须注释说明 |
| 6 | **改完即验证** | 每改一处→编译→API验证→浏览器验证禁止累积 |
---
## 二、验证层级
| 层级 | 执行时机 | 命令/方法 | 通过标准 |
|------|----------|-----------|----------|
| **L1 编译** | 每次代码改动后 | `dotnet build src\CncWebApi\CncWebApi.csproj` + `cd frontend && npm run build` | 两个命令退出码0 |
| **L2 API** | 改了后端后 | PowerShell调用改动过的API | HTTP 200 + `code=0` + `data`非null + 关键字段非空 |
| **L3 浏览器** | 改了前端后 | `cd frontend && npx playwright test e2e/smoke-iis.spec.ts --project=chromium` | 对照5.0维度总表逐项通过 |
| **L4 发布** | 交付前 | `appcmd recycle apppool "haoliang"` + 重复L2+L3 | 全部通过 + 局域网可访问 |
> 编译通过 ≠ 功能正确。L1只是最基本的大门。
---
## 三、前后端接口契约验证
每次新增或修改API返回字段时必须执行
| 检查项 | 通过标准 |
|--------|----------|
| DTO字段名 vs API实际返回JSON | 一致考虑camelCase序列化`TotalQuantity` → `totalQuantity` |
| API返回字段名 vs 前端绑定 | 完全一致或兼容(`??` 回退) |
| 返回数据结构 | 前端取值路径正确(`res.data.items` vs `res.data` |
| 空值处理 | 前端显示占位符(`-` 或 `暂无数据`),不白屏不报错 |
| 分页格式 | 数组 `[{...}]` vs 分页对象 `{items:[], total:0}` 前后端一致 |
| 枚举值 | 前端处理整数和字符串两种情况 |
| DateTime | 前端兼容 `"2026-05-03T00:00:00"``"2026-05-03"` |
---
## 四、Playwright E2E测试标准
> **强制要求每次编写Playwright测试必须逐项对照5.0维度总表。每个维度有测试用例或注释说明跳过原因。**
### 5.0 测试维度总表(每个页面必查)
| # | 维度 | 必须断言 | 适用 | 禁止 |
|---|------|----------|------|------|
| 1 | 页面加载 | URL正确 + 关键元素可见 | 所有页面 | 只goto不等待 |
| 2 | 默认数据 | 打开即有数据(行数>0 / 卡片有值) | 有数据展示的页面 | 不检查数据 |
| 3 | 汇总卡片数值 | ≠ 空 ≠ `-``--``undefined``null` | 有统计卡片的页面 | 只检查可见 |
| 3.1 | 图表有数据 | Canvas/SVG已渲染 + 有数据点/色块(非空白) | 有ECharts/Chart组件的页面 | 只检查容器可见 |
| 4 | 表格行数 | `.el-table__row` > 0 | 有表格的页面 | 只检查table可见 |
| 5 | 表格每列有值 | 第一行逐列文本非空 | 有表格的页面 | 只检查前2-3列 |
| 6 | 时间/数值列 | 有具体数值非空非NULL | 有时间/数值列的页面 | 跳过 |
| 7 | 状态标签 | 为已知中文文案(正常/离线/缺失/告警) | 有状态列的页面 | 不检查文本 |
| 8 | 下拉框展开 | 选项≥1 + 每项文本非空非占位 | 有下拉框的页面 | 只检查select可见 |
| 9 | 下拉框选择 | 选中→显示label→查询→筛选生效 | 有下拉框的页面 | 不验证筛选效果 |
| 10 | 筛选生效 | 选条件→查询→表格数据变化 | 有查询功能的页面 | 不验证变化 |
| 11 | 文本筛选 | 输入关键词→查询→结果每行包含关键词 | 有搜索框的页面 | 不验证匹配度 |
| 12 | 日期筛选 | 切换日期→查询→数据变化 | 有日期选择的页面 | 不验证效果 |
| 13 | 重置按钮 | 所有筛选恢复默认占位符 | 有重置按钮的页面 | 不验证清空 |
| 14 | 分页切换 | 第2页高亮 + 表格第一行数据变化 | 数据>1页 | 只检查组件可见 |
| 15 | 弹窗打开 | 可见 + 标题正确 + 表单有初始值 | 有弹窗的页面 | 不验证标题和内容 |
| 16 | 弹窗提交闭环 | 填写→提交→成功提示→列表刷新→数据变更 | 有写操作弹窗 | 只验证能打开 |
| 17 | API vs 页面对账 | 调API拿JSON→对比页面显示值 | 有统计卡片的页面 | 不对比API值 |
| 18 | 下拉选项值有效性 | 每项文本非空、value可选中 | 有下拉框的页面 | 不验证选项内容 |
| 19 | 空数据状态 | 不可能的条件→行数=0 + 不白屏不报错 | 有筛选的页面 | 不验证极端情况 |
| 20 | 错误处理 | 异常场景→友好提示不白屏 | 所有页面 | 不考虑异常 |
### 5.1 页面类型速查
| 页面类型 | 必须覆盖维度 |
|----------|-------------|
| 列表页(表格+筛选) | 1,2,4,5,8,9,10,13,14,19 |
| 列表页(表格+筛选+卡片) | 1,2,3,4,5,6,7,8,9,10,13,14,17,19 |
| 仪表盘/概览页 | 1,2,3,3.1,5,6,9,12,17,20 |
| 详情/表单页 | 1,15,16 |
| 所有页面 | 1,20 |
### 5.2 交互控件矩阵
| 控件 | 验证动作 | 通过标准 |
|------|----------|----------|
| 下拉框 | 展开→选择→查询 | 选项≥1显示选中label筛选生效 |
| 日期选择器 | 选择日期 | 输入框显示 `YYYY-MM-DD` |
| 查询按钮 | 点击 | 表格刷新 |
| 重置按钮 | 点击 | 所有筛选恢复默认 |
| 分页 | 切换页码 | 数据变化,页码高亮 |
| 弹窗-打开 | 点击触发 | 可见,标题正确,表单有初始值 |
| 弹窗-提交 | 填写→提交 | 成功提示,列表刷新 |
| 导出 | 点击 | 触发下载或提示 |
### 5.3 数据展示矩阵
| 元素 | 验证标准 |
|------|----------|
| 汇总卡片 | 非空 ∧ ≠ `""` ∧ ≠ `"-"` ∧ ≠ `"--"` ∧ ≠ `"undefined"` ∧ ≠ `"null"` |
| 图表(ECharts等) | Canvas/SVG存在 + 非空白(有数据点/扇形/柱状) |
| 表格数据列 | 第一行每列有文本值 |
| 状态标签 | 已知中文文案 |
| 时间列 | 有值且格式正确 |
| 空状态 | 显示"暂无数据" |
### 5.4 断言示例
```typescript
// 数值断言(排除空/占位符)
const text = await locator.textContent()
expect(text).toBeTruthy()
expect(text!.trim()).not.toBe('')
expect(text!.trim()).not.toBe('-')
expect(text!.trim()).not.toBe('--')
// 下拉框断言(展开+选项)
const options = page.locator('.el-select-dropdown__item')
expect(await options.count()).toBeGreaterThan(0)
// 表格断言(行数+列值)
const rows = page.locator('.el-table__body-wrapper .el-table__row')
expect(await rows.count()).toBeGreaterThan(0)
```
---
## 五、API验证方法
### 登录获取Token
```powershell
$loginRes = Invoke-RestMethod -Uri "http://127.0.0.1/api/admin/login" -Method Post -ContentType "application/json" -Body '{"username":"admin","password":"admin123"}'
$token = $loginRes.data.token
$headers = @{ Authorization = "Bearer $token" }
```
### 必须非空字段
| API端点 | 必须非空字段 |
|----------|-------------|
| `/admin/production/daily-summary` | `totalQuantity`, `activeMachineCount`, `totalCuttingTime`, `avgQuantityPerMachine` |
| `/admin/production/daily` | `items[].machineName`, `items[].totalQuantity`, `items[].date` |
| `/admin/workshop/list` | `items[].value`, `items[].label` |
| `/admin/machine/list` | `items[].value`, `items[].label` |
| `/admin/worker/list` | `items[].value`, `items[].label` |
| `/admin/dashboard/statistics` | 各统计卡片字段 |
| `/admin/collector/status` | `status` |
---
## 六、发布前Checklist
- [ ] `dotnet build src\CncWebApi\CncWebApi.csproj` → 0错误
- [ ] `cd frontend && npm run build` → 0错误
- [ ] 前端build输出已复制到 `src\CncWebApi\admin\`
- [ ] 回收AppPool`appcmd recycle apppool "haoliang"`
- [ ] 改动过的API端点用PowerShell调用返回值正确
- [ ] Playwright冒烟测试通过`cd frontend && npx playwright test e2e/smoke-iis.spec.ts --project=chromium`
- [ ] `git add` + `git commit -m "中文描述"` + `git push`
---
## 七、反模式清单
| # | 禁止 | 必须改为 |
|---|------|----------|
| 1 | 编译通过 = 测试通过 | 验证功能 |
| 2 | 只检查 `toBeVisible()` | 检查文本值,排除空/占位符 |
| 3 | SQL写 `NULL AS 字段名` 占位 | 实现或抛异常 |
| 4 | DTO加字段不填充值 | 追踪到Service层实现填充 |
| 5 | 改完代码不发布到IIS | 每次改动发布后验证 |
| 6 | Playwright不操作控件 | 模拟用户操作:点击、选择、输入 |
| 7 | 累积改动后批量验证 | 改完一处立即验证 |
| 8 | 前端凭假设绑定字段名 | 对照API实际返回值 |
| 9 | 硬编码配置值 | 从配置文件读取 |
| 10 | 不检查组件库格式要求 | 查阅文档(如 dayjs `YYYY-MM-DD` |
| 11 | 测试不按维度表逐项覆盖 | 对照5.0维度总表 |
| 12 | 测试只覆盖部分页面 | 每个改动涉及的页面都要覆盖 |
| 13 | 新增维度后不更新维度总表 | 新发现的问题必须补充为新维度 |
| 14 | 图表只检查容器DOM存在 | 验证Canvas/SVG有实际渲染内容数据点/色块) |