如何验证 UI 显示的存储用量与后端数据一致?
推荐方法:保留真实链路测试,补充分层测试
建议保留现有的端到端测试:以已验证的内部 API 为数据基准,让 UI 请求经过真实中间层,再核对页面显示。这能检查中间层是否传错或算错数据。
浏览器响应 mock 适合检查前端如何显示指定数据,也方便覆盖边界值和异常场景。不过,它绕过了真实服务链路,无法单独证明 UI 与内部 API 的数据一致。
当前最需要调整的是预期值的计算方式。测试应根据明确的单位和格式规则确定预期值,避免复制生产代码的转换逻辑。否则,测试和生产代码可能犯同样的错误,测试依然通过。
不同测试分别覆盖什么
假设数据经过以下链路:
内部 API → 中间服务 → UI 使用的 API → 前端格式化 → 页面显示
| 测试方式 | 验证范围 | 主要用途 |
|---|---|---|
| 浏览器响应 mock | 前端格式化与渲染 | 单位切换、小数位、空值、请求失败 |
| 中间层集成测试 | 内部 API 数据到 UI API 响应的处理 | 字段映射、单位转换、聚合、权限过滤 |
| 真实链路端到端测试 | 内部 API 到页面显示 | 验证各层连接后,页面与基准数据一致 |
如果真实存储环境难以控制,可以在内部 API 的响应边界提供固定数据,让后面的中间服务和前端照常运行。这能覆盖中间层的处理逻辑,但测试范围仅从数据注入的位置开始,仍需保留少量真实链路测试,验证实际数据接入。
实施步骤
1. 先明确比较的口径
至少需要确认:
- UI 显示的是租户用量、卷用量,还是整个文件系统的用量。
- 配额字段表示业务配额,还是底层存储总容量。
- API 返回的是字节数,还是已经转换过的数值。
- 页面使用 MiB/GiB,还是 MB/GB。
- 单位如何切换,保留几位小数,以及如何舍入或截断。
- 数据是否经过缓存,允许多长的刷新延迟。
二进制单位的换算为:
1 MiB = 2^20 bytes = 1,048,576 bytes
1 GiB = 2^30 bytes = 1,073,741,824 bytes
这些规则应以产品约定或接口契约为准。仅凭“当前实现就是这么算的”,无法判断实现是否正确。
2. 使用可控且稳定的数据
优先使用独立的测试租户或测试卷,通过真实业务流程或测试环境的数据准备机制建立已知状态,再等待内部 API 的统计结果稳定。
共享存储上的后台写入、异步统计和缓存刷新,可能导致 API 与 UI 读到不同时间的数据。测试时应尽量停止并发写入,并在约定的刷新期限内等待页面更新。
如果系统提供统计时间戳或版本号,可以用它们确认 API 与 UI 对应同一份统计结果。如果只是先后读取两次,既没有快照,也没有稳定的环境,就无法保证两次数据严格对应同一时刻。
3. 独立确定预期值
固定场景可以直接写出经核对的输入和预期显示值,便于发现转换错误。例如,约定达到 1 GiB 时切换单位,并保留一位小数:
| 原始字节数 | 预期显示 |
|---|---|
0 | 0.0 MiB |
1048576 | 1.0 MiB |
1073741824 | 1.0 GiB |
1610612736 | 1.5 GiB |
单位阈值附近的数值、舍入边界和大数值,可以在格式化单元测试或 mock 测试中充分覆盖。端到端测试保留少量有代表性的场景即可。
如果必须比较环境中的动态值,可以根据上述约定编写独立的测试换算函数,再用固定用例校验。不要直接导入生产格式化函数来生成预期值。
Playwright 示例:通过真实请求核对页面
示例的运行前提是:
- 测试环境已准备好稳定数据,用量为
1610612736字节,配额为10737418240字节。 - 页面按二进制单位显示,保留一位小数。
- Playwright 已配置
baseURL,API 请求与页面访问同一个测试租户。 - 页面会在正常加载或自身刷新后显示最新统计结果。
接口路径、字段和 data-testid 都是示例,需要替换为实际定义。
import { test, expect } from '@playwright/test';
test('存储页面与内部 API 的数据一致', async ({ page, request }) => {
// 数据准备应在测试前完成,并等待统计稳定。
const response = await request.get('/internal/storage');
expect(response.ok()).toBeTruthy();
const storage = await response.json();
// 确认基准数据符合本用例的前提。
expect(storage).toMatchObject({
usedBytes: 1610612736,
quotaBytes: 10737418240,
});
// 不拦截页面请求,让请求经过真实的中间服务。
await page.goto('/storage');
// 预期文本依据显示规则预先核对。
await expect(page.getByTestId('storage-used'))
.toHaveText('1.5 GiB');
await expect(page.getByTestId('storage-quota'))
.toHaveText('10.0 GiB');
});
使用预先核对的显示值,可以验证已知数据经过真实链路后是否显示正确,也省去了在测试中重写复杂转换逻辑的工作。
toHaveText() 会重试断言,但不会主动让页面重新请求数据。如果页面只在加载时读取一次,应在数据稳定后再打开页面;如果要测试刷新行为,就执行产品实际提供的刷新操作。等待期限应根据统计刷新约定配置,避免随意添加固定时长的 sleep。
df -h 能否作为基准
只有当 UI 与 df 统计的是同一个文件系统,且用量含义相同时,二者才适合比较。
df 报告文件系统空间信息,业务 API 可能统计的是租户配额、逻辑文件大小或计费容量。压缩、去重、快照和文件系统元数据等因素,也可能让这些数值不同。租户配额尤其不能直接等同于文件系统总容量。
df -h 的输出已经过单位转换和舍入,不适合作为精确断言的原始值。在 GNU df 环境中,可以按字节单位输出指定挂载路径的信息:
df -B1 --output=size,used,avail /path/to/mount
这些选项适用于 GNU df,其他系统需使用对应实现支持的方式。命令的执行位置也必须与被测存储对应,以免读到宿主机或其他容器的文件系统。
如果还需要验证内部 API 的数据是否符合底层存储统计,应单独建立“存储统计到内部 API”的测试。页面数据不一致时,就可以结合各段测试定位问题。
注意事项
- 格式测试应同时检查数值、小数位和单位。只比较数字,可能漏掉 MiB/GiB 标错的问题。
- 舍入造成的精度损失与异步刷新造成的数据变化,应分别处理。前者按显示规则断言,后者通过稳定数据和有期限的等待解决。
- 不要随意加入“允许误差 5%”之类的容差。如果业务确实允许偏差,应写清依据、范围和适用场景。
- 用于计算的字节数如果可能超过 JavaScript 的安全整数范围,需要确认接口如何表示该数值,避免用
Number处理后丢失精度。 - 测试失败时,保留内部 API 原始响应、UI API 响应、页面显示和采样时间,便于判断问题出在数据源、转换、缓存还是渲染。
备注:内容仅供参考。
