ArkTS + HarmonyOS 三方依赖基线:网关稳定性与回归证据(连载05/06)【共创稿事节】
三方库升级以后,应用能顺利安装,首页也能打开,却不一定说明这次升级可靠。请求成功时的数据形状可能变化;异常对象中的字段可能发生调整;相邻的两个异步请求可能乱序返回;离线、超时与解析失败时的提示策略也可能走向不同分支。与其等版本上线以后再追日志,不如把这些行为固定为一套可重复的契约检查,让升级是否可接受有具体证据。

在此前四篇中,工程建立了 CatalogPort 作为业务接口,AxiosCatalogGateway 封装 @ohos/axios,把第三方异常统一成 CatalogLoadError,又通过 CatalogCoordinator 的请求代次规避了旧请求覆盖新数据。随后引入了 ohpm 依赖更新与锁文件审查。现在需要补上的不是另一个“万能网络层”,而是针对这些已有模块的回归机制:依赖版本发生变化,哪些行为必须保持一致;如果观察到变化,怎样准确定位在请求适配、结果解析还是界面呈现。
一、先看演进路线,而不是先测速度
工程早期,页面可以直接调用 axios.get()。这种写法在简单示例中很短,却容易让页面直接依赖 Axios 的响应结构与异常字段。加入 CatalogPort 后,页面只知道“请求目录数据并得到 CatalogItem[]”,具体网络库交由网关处理;再加入 CatalogCoordinator,异步结果写入状态之前还要检查请求代次。到了依赖升级阶段,治理对象也从单个版本号扩展为“依赖清单、锁文件、业务契约、故障恢复”四个相互关联的内容。

oh-package.json5 的版本范围表达允许哪些版本被安装,oh-package-lock.json5 则用于固定具体依赖解析结果。官方 ohpm update 会根据 semver 约束更新依赖,不同的 ^ 与 ~ 约束范围并不相同。评估升级必须同时记录锁文件的实际变化,不能只看到配置里版本字段没有改变便认定依赖完全一致;也不能把 ohpm clean 当作无风险的重装命令,因为默认清理流程会移除锁文件,需要保留时应采用官方支持的 --keep-lockfile 选项。
稳定性基线应先回答行为问题,再回答性能问题。比如重复请求同一份合法数据是否生成相同的业务条目;接口返回字段缺失时是否转换为格式错误;可恢复的网络故障是否给出重新尝试的路径;连续切换列表筛选条件时能否阻止过时请求改写界面。这些都是可在测试替身中稳定控制的事件,不依赖偶然的公网延迟。
二、定义可注入的响应与故障脚本
下面的代码聚焦业务契约层,使用 ArkTS 类与 Promise 实现一个可注入的目录端口。业务返回结构沿用前文的 CatalogItem,错误类别沿用 CatalogLoadError 的语义,测试替身本身不依赖 Axios 的内部类。这样即使将来更换三方库,回归脚本仍可保留;真正的 AxiosCatalogGateway 只需继续符合端口约定。
class CatalogItem {
readonly id: string;
readonly title: string;
constructor(id: string, title: string) {
this.id = id;
this.title = title;
}
}
type CatalogErrorKind = 'NETWORK' | 'TIMEOUT' | 'HTTP' | 'FORMAT';
class CatalogLoadError extends Error {
readonly kind: CatalogErrorKind;
readonly retryable: boolean;
constructor(kind: CatalogErrorKind, message: string, retryable: boolean) {
super(message);
this.kind = kind;
this.retryable = retryable;
}
}
interface CatalogPort {
list(): Promise<CatalogItem[]>;
}
class FixtureCase {
readonly mode: string;
readonly items: CatalogItem[];
constructor(mode: string, items: CatalogItem[]) {
this.mode = mode;
this.items = items;
}
}
class ScriptedCatalogPort implements CatalogPort {
private cursor: number = 0;
constructor(private cases: FixtureCase[]) {}
async list(): Promise<CatalogItem[]> {
const index: number = this.cursor++;
const item: FixtureCase | undefined = this.cases[index];
if (item === undefined) {
throw new CatalogLoadError('FORMAT', '测试输入已用尽', false);
}
if (item.mode === 'timeout') {
throw new CatalogLoadError('TIMEOUT', '请求超时', true);
}
if (item.mode === 'offline') {
throw new CatalogLoadError('NETWORK', '网络不可用', true);
}
if (item.mode === 'invalid') {
throw new CatalogLoadError('FORMAT', '目录字段不符合契约', false);
}
return item.items;
}
}
class CatalogCoordinator {
private generation: number = 0;
items: CatalogItem[] = [];
error: CatalogLoadError | undefined;
constructor(private port: CatalogPort) {}
async refresh(): Promise<void> {
const current: number = ++this.generation;
try {
const next: CatalogItem[] = await this.port.list();
if (current !== this.generation) { return; }
this.items = next;
this.error = undefined;
} catch (e) {
if (current !== this.generation) { return; }
this.error = e instanceof CatalogLoadError ? e :
new CatalogLoadError('NETWORK', '目录加载失败', true);
}
}
invalidate(): void { this.generation++; }
}
这段实现将行为边界表达为可以检查的类与接口。ScriptedCatalogPort 按顺序给出成功、离线、超时或格式失败,不会生成假网络日志。CatalogCoordinator 只接收当前代次的结果,在异步等待之后仍检查 generation;这和 B03 的并发保护属于同一工程主线,没有重复引入另一套竞争规则。生产环境仍通过 AxiosCatalogGateway 实现 CatalogPort,并使用实际接口响应完成格式验证。若接口契约中的必填字段变化,应由网关转换逻辑处理,而不是让页面四处分散字段兼容分支。

测试时应分别执行以下场景:合法列表返回后 items 正确更新且错误状态清除;离线和超时后保留上一次可用内容,同时展示可重试提示;格式错误时阻止不合法数据进入状态;先发的慢请求晚于后发请求完成时不覆盖新数据。最后一种场景需要一个由测试代码显式控制 Promise 完成次序的端口替身,不应依赖 setTimeout 或真实服务器运气。上述脚本中展示的是独立单次故障分类,跨代次顺序测试需另配受控延迟端口。这样测试失败时才能对应到明确的条件,而不是仅凭“页面偶尔乱了”判断。
三、性能基线必须和稳定性基线分开
当契约检查全部通过,才有意义讨论升级前后的响应处理开销。把单次端到端耗时拆为网络等待、响应解码、字段校验、状态提交和 UI 更新,更容易找到瓶颈。使用真实网络的结果应同时记录网络条件与服务端负载;仅在本地替身内进行的测试,只能评估本地业务逻辑,不可以推断生产网络延迟或实际用户体验。Date.now() 的差值可用于粗略记录同步步骤,但不应该被当作精确帧耗时;高频采样更应考虑系统 Profiler 和不同设备上的重复结果。
建议基线报告保留三类数据:环境元信息、输入序列及执行结果。环境元信息包括 HarmonyOS SDK/设备/构建模式、ohpm 客户端版本、依赖清单和锁文件摘要;输入序列记录成功、失败、超时与乱序的可重复脚本;执行结果记录成功断言数、失败断言的具体原因、异常类别分布和实际测到的时间数据。没有运行过的项目就留下“待采集”,不填漂亮的百分比。这样后续版本升级才能与同一条件下的历史样本进行横向比较。
这一阶段还要区分“依赖变化”与“输入数据变化”。如果升级前用的是两条固定目录数据,升级后用动态十万条真实数据,即使测试结果看起来截然不同,也无法归因于三方库。比较时应固定输入格式、条目数量、处理链路以及异常序列。如果业务确实同步调整了字段模型,就同时存放旧格式和新格式样本,明确这次是兼容性迁移,而不是默默扩大性能测试的变量数量。
四、把结果变成升级前的决策门禁
升级审批不该只靠“构建通过”和“页面能打开”。更稳妥的顺序是:锁定对比版本、核对锁文件差异、执行端口契约检查、运行故障注入矩阵、复核并发与取消策略、采集受控环境的性能样本、最后进行带有真实 UI 的交互检查。步骤之间存在依赖,上一关失败就先定位原因,而不是把若干缺陷合并成一条“回归待观察”。

需要强调的是,故障种类并不等于用户提示文案。一次网络断开可以提示重新连接,而格式错误应该更接近数据或接口契约问题;如果把这些情况统一映射为“网络繁忙,请重试”,会让用户反复点击却始终不能成功。retryable 是网关提供给上层策略的信号,不应自动等价为立即无限次数重试。协调器仍需限制重复操作,页面也需要保持清楚的加载、成功与失败状态,并考虑用户主动离开页面后的结果失效。
当检测到回归失败,回退流程应同时恢复 oh-package.json5 和 oh-package-lock.json5,通过工程版本管理核对改动,再重新安装、构建并重新执行失败用例。只改一个版本号但留下新的锁文件,不是一条可审计的回退路径。对于依赖升级引发的字段转换变化,建议把最小失败样本纳入永久回归集,而不是在本次修复后删掉它。
五、旧体系到新体系,变化究竟发生在哪里
从直接调用 Axios 到隔离端口,再到请求代次控制、依赖回退和基线审查,真正演进的是系统的可解释性。任何一次失败都应该能被定位到具体输入、具体端口行为和具体状态提交阶段。这样的成本并不是为了抽象而抽象:当某个库的版本变化导致异常格式改变时,核心页面无需跟着修改;当某次弱网测试暴露过时请求覆盖时,修改集中在协调器;当性能出现退化时,也可以先复核原始输入与版本证据,再决定是否下钻到 ArkUI。
下一篇将以这套基线为基础,完善安全与工程质量门禁。第三方库不是升级完成就可以不再关注的黑盒;依赖树、锁文件、网络接口、错误映射和测试证据需要共同组成可追溯的维护链条。
官方参考:
更多推荐


所有评论(0)