Pangolin 统一测试框架 · 架构说明
分层方法论 + 脚手架模板 + 单一编排入口 · 可自动化 / 可量化 / 可自定义
定性(方案 A):不存在一个能同时覆盖「界面渲染 / 交互驱动 / 后端记账 / 真链路端到端」的单一工具——这四者运行时根本不同。所以「一套测试框架」= 一套
分层方法论 + 一组
可复用脚手架模板(假桥、Provider 覆盖、golden+字体锁、Go 真库套、E2E compose 骨架)+ 一个
统一编排入口。跨多端项目的复用方式是
模板复制后按接缝适配,而非全项目 import 同一个包。
设计原则
- 测试金字塔:底层用例多、快、稳、便宜;越往上越接近真实、越慢、越脆。绝大多数断言压在底部,顶部只留薄薄一层冒烟。
- 契约接缝(contract seam)优先:系统由若干跨进程/跨语言的接缝拼成(原生→Dart、Dart→控制面、agent→控制面)。每段接缝先有契约快照守住「字段形状」,再分层在接缝两侧各自下刀。
- 量化优先:每层都产出可比对的数字(覆盖率、像素不一致比例、数值容差、时延 ms、通过率),而非「人眼看着对」。
- 单一编排:一个入口(
make test / test.sh)按 profile 跑对应层,输出一份合并报告。
前不久的「mac 实时统计恒为 0」就是典型反例:原生侧产出统计、Dart 侧消费统计,两端的 pangolin/vpn/stats 契约没有任何自动化测试守门,触发条件一变(隧道已连接才启动 app)就静默失效。L0 契约层防的正是这类 bug。
① 分层测试金字塔
图 1 · 五层金字塔:L0 契约为地基,L1→L4 自底向上
② 契约接缝数据流
统计/记账数据沿一条跨进程链路流动。每段接缝标注其契约,以及哪一层测试在此下刀——这张图就是「该测什么、在哪测」的地图。
图 2 · 契约接缝:从节点数据面一路回到客户端 UI
实时上下行走 ①(libbox→EventChannel→Dart);累计流量/周柱走 ④→③→DB→②(节点统计→agent→控制面入库→客户端拉取)。两条流共用同一张接缝图,测试各自归位。
③ 四类诉求 → 层 → 工具 → 量化指标
把你提出的四类测试诉求逐条落到层、工具、量化指标与现状:
| 诉求 | 层 | 工具 | 量化指标 | 现状 |
① 界面设计一致 每端一致 |
L3 |
Flutter golden(字体锁定,host 无关→天然每端一致);design-distill pixelmatch 对照 HTML 原型真相源 |
像素不一致比例 vs 阈值 0.05;golden 通过/失败 |
素材齐 · CI 没跑 |
② 前端交互 连接 / 登陆 / 登出 |
L2 |
flutter_test 进程内驱动整树 + VpnBridgeMock + http MockClient + ProviderScope 覆盖 |
流程通过/失败;覆盖率 lcov |
素材齐 · 待组装 |
③ 数值型 统计 / 实时速度 / 时延 |
L1 + L4 |
L1:格式化/换算纯逻辑单测;L4:连真节点拉 stats 断言 bytes 增长、延迟 >0 |
实测 vs 期望容差;时延 ms;速率 B/s >0 |
L1 可补 L4 缺 |
④ 后端网络 按租户流量 / 限流 |
L2 + L4 |
L2:SQLite 真库 + testcontainers MySQL(已覆盖并发/重放/跨日/配额);L4:全链路真流量计数 |
记账数值断言;限流是否触发;覆盖率 go cover |
L2 强 L4 缺 |
④ 逐层详解
L0 · 契约 / 静态闸
守住接缝的「字段形状」
- 测什么:跨进程契约的结构——
pangolin/vpn/stats EventChannel 字段、gRPC UsageEntry proto、/v1/me·/v1/usage JSON 形态;静态:analyze/vet、OpenAPI 结构、红线词。
- 工具:契约快照测试(snapshot)+ 已有 OpenAPI 校验 +
ci/scan-redline.sh。
- 量化:字段 diff = 0;扫描命中 = 0。
- 现状/缺口:OpenAPI/红线已有 契约快照需补。这是最被低估、性价比最高的一层。
- 可自定义:新增一段接缝 = 加一份快照基线。
L1 · 单元
纯逻辑,最快最稳
- 测什么:数值换算(bytes→速率/时长格式化)、状态机(ConnectionController)、记账纯函数(GB 卡控、dp_uuid 解析)。
- 工具:
flutter_test;go test + testify。
- 量化:覆盖率(lcov /
go test -cover)。
- 现状:已有(前端 controller、后端 usage 包多场景)。
L2 · 集成 / 交互
带假替身的真实路径
- 前端交互:
pumpWidget(真 App 树) 驱动 登陆→重启续登→点连接→状态流转→登出,把原生隧道换 VpnBridgeMock、服务端换 MockClient,ProviderScope 覆盖注入。
- 后端 DB:SQLite 内存真库 + testcontainers MySQL/Redis(已覆盖并发去重/重放/跨日分桶/配额)。
- 后端 HTTP/gRPC:
httptest.Server(鉴权路径需补);gRPC bufconn + 自签 CA/CRL(已强)。
- 量化:通过/失败 + 覆盖率 + 数值断言。
- 现状:后端强 前端 flow 待组装 · HTTP 鉴权偏薄。
L3 · 视觉一致性
每端渲染一致 + 对照设计真相源
- 测什么:mobile/tablet/desktop × light/dark × zh/en 的渲染一致;以及实现 vs HTML 原型的像素差。
- 工具:Flutter golden(
flutter_test_config.dart 字体锁定→host 无关,所以「每端效果一致」天然成立);design-distill shoot-prototype.mjs + diff.mjs(pixelmatch)。
- 量化:像素不一致比例 vs 阈值
0.05。
- 现状:20+ golden 基准已有 CI 未接 · pixel 闸未自动化。
- 可自定义:新增屏 = 加一张基线;阈值按屏可调。
L4 · 端到端冒烟
真链路:统计真不真 / 流量真不真
- 测什么:起真控制面 + agent + sing-box + DB,发真流量,断言按租户/按设备 bytes 入库、限流触发、实时数值 >0。
- 工具:docker-compose + 探针驱动脚本(见图 5)。
- 量化:实测 vs 期望容差、限流是否触发、连上耗时、时延 ms。
- 现状:完全缺——最大缺口。
local_test.sh 只构建启动、deploy.sh 只部署,均无连通性自检。
- 边界(建议):先做 (a) 后端全链路(可全自动、进一台 Linux runner);(b) desktop 真连(需常驻 Mac)与 (c) iOS/Android 真机 作本地按需后续。
⑤ 编排与 CI 流水线
一个入口按 profile 分流:CI 每 push 只跑快而稳的 fast;视觉与 E2E 按需触发。
图 4 · 单一入口 → 三 profile 泳道
⑥ L4 端到端冒烟拓扑
图 5 · docker-compose 真链路 + 测试驱动器(推荐边界 a)
⑦ 量化指标总表
| 维度 | 指标 | 工具 / 口径 |
| 覆盖率 | 行/分支 % | flutter test --coverage + go test -coverprofile → 合并 lcov |
| 视觉 | 不一致像素比例 vs 阈值 0.05 | pixelmatch(diff.mjs);golden 通过/失败 |
| 数值精度 | 实测 vs 期望容差 | L4 断言 bytes/速率/累计落在容差带内 |
| 时延 | 毫秒 ms | urltest 延迟 >0 且在合理区间 |
| 稳定性 | 通过率 / flaky 率 | 多次重跑统计;flaky 用例隔离 |
⑧ 可复用与可自定义
跨项目模板(复制即用,按接缝微调):
- 假桥模式(
VpnBridge 接口 + Mock)→ 任何「原生/外部依赖」都可照此切假。
- ProviderScope/DI 覆盖注入 → 任何状态层都可注入假数据。
- golden + 字体锁定(
flutter_test_config.dart)→ 视觉回归通用骨架。
- Go 内存真库套(
run_sqlite_test.sh)+ testcontainers 集成套 → 任何 DB 项目通用。
- E2E docker-compose 骨架 + 探针驱动器 → 换服务清单即复用。
- 单一编排器 + profile 分流 + 合并报告 → 框架的「壳」,跨项目不变。
每项目按接缝定制:契约字段、API 路径、compose 服务清单、golden 屏清单、数值容差阈值。
⑨ 落地路线(建议)
| 序 | 动作 | 性价比 | 起点 |
| 1 | 补 L0 契约快照 + 把现有 golden 接进 CI | 最高(防回归) | 素材已有,组装即可 |
| 2 | 组装 L2 前端 flow 测试(登陆/续登/连接/登出) | 高 | Mock/覆盖素材全齐 |
| 3 | 建 L4 后端全链路冒烟 (a) | 中(覆盖"真不真") | 需新建 compose + 驱动器 |
| 4 | 补 L1 数值单测 + L2 HTTP 鉴权测试 | 中 | 增量补齐 |
| 5 | (b) desktop 真连 / (c) 真机——本地按需 | 低(贵·脆) | 需常驻 Mac / 真机 |
本文档仅为架构说明(设计真相源)。具体落地拆解(契约快照实现、前端 flow 用例、L4 冒烟脚本)走各自实现计划(writing-plans)。