Appium 事件时序(Event Timings):用 `appium:eventTimings` 与 Events API 精确度量会话与命令耗时 Appium 事件时序Event Timings用appium:eventTimings与 Events API 精确度量会话与命令耗时【免费下载链接】appiumCross-platform automation framework for all kinds of apps, built on top of the W3C WebDriver protocol项目地址: https://gitcode.com/GitHub_Trending/ap/appiumAppium 内置了一套事件时序Event Timings采集机制可以在会话过程中记录何时发生了什么以及每条命令耗时多长是定位会话启动慢、命令执行慢等性能问题的利器。本文基于packages/appium/docs/zh/guides/event-timing.md展开结合packages/base-driver源码与测试完整讲解appium:eventTimings能力、events响应结构、自定义事件上报以及常见分析用法读完即可在自己的 Appium 测试工程中落地一套命令级耗时统计方案。一、概述Appium 能记录哪些时序信息Appium 具备记录两类时序信息的能力会话启动startup阶段的节点事件例如新会话被请求、新会话已启动每个事件会记录其发生的精确时间戳命令command执行耗时会话期间执行的每一条 Appium 内部命令都会记录其开始处理与结束处理的时间。这是一个高级advanced功能需要显式开启。其开关是一个名为appium:eventTimings的 capability将其设为true即可在会话期间采集事件时序。注意从当前仓库的会话能力文档packages/appium/docs/zh/reference/session/caps.md可以看到appium:eventTimings目前被标记为deprecated已弃用官方建议改用getLogEvents端点来获取事件时序数据。不过该 capability 的语义、响应结构及数据格式仍然与下方讲解一致理解它可以帮你解读历史测试数据与老代码。二、开启能力appium:eventTimingscapability在创建会话时通过 desired capabilities 传入该能力即可{ platformName: Android, appium:automationName: UiAutomator2, appium:eventTimings: true }开启后调用POST /session/:id/appium/events即各语言客户端中的driver.logs.events或类似方法具体名称视客户端而定时其响应会被额外装饰decorate一个events属性其中包含会话截至目前的所有事件时序数据。客户端调用示意以 JS 客户端WebdriverIO / Appium 官方客户端为例典型调用路径是// 通过 logs API 获取事件时序 const events await driver.logs.events(session);不同客户端的方法名可能略有差异请以你所使用客户端文档为准。重要前提只能拿到已经发生的事件需要特别强调/session/:id/appium/events只返回在调用发生那一刻之前已经记录的事件。因此想获取整个会话的完整时序最佳调用时机是即将退出会话quit之前在会话中途多次调用可以拿到会话不同阶段的增量数据。三、events响应结构详解开启 capability 后/session/:id/appium/events的响应会被装饰为如下结构events属性{ event_type: [occurence_timestamp_1, ...], commands: [ { cmd: command_name, startTime: js_timestamp, endTime: js_timestamp } ] }events属性内部包含两类子属性以事件类型命名event type的属性其值为一个时间戳数组记录该事件每次发生的时间。因为同一事件在一个会话中可能发生多次例如会话期间多次重启应用、多次触发某自定义事件所以用数组保存全部发生时刻。commands属性固定存在的命令执行记录是一个对象数组每个对象包含cmdAppium 内部命令名例如click、findElement等startTime命令开始处理的时间戳JavaScript 毫秒时间戳endTime命令处理结束的时间戳JavaScript 毫秒时间戳。事件类型示例Appium 基础驱动内置了几类会话级事件。在 packages/base-driver/lib/basedriver/driver.ts 中可以看到这些事件的常量定义事件常量事件类型名event_type触发时机EVENT_SESSION_INITnewSessionRequested新会话被请求创建会话命令开始执行时EVENT_SESSION_STARTnewSessionStarted新会话启动完成创建会话命令执行完毕时EVENT_SESSION_QUIT_STARTquitSessionRequested退出会话被请求删除会话命令开始执行时EVENT_SESSION_QUIT_DONEquitSessionFinished退出会话完成删除会话命令执行完毕时这些事件在executeCommand的入口与出口分别通过this.logEvent(...)记录见 driver.ts 与 driver.ts因此newSessionRequested与newSessionStarted两个时间戳之间的差值就是一次新会话从被请求到完全建成的耗时。事件类型并非只有内置的几种需要明确各个 driver 会自定义自己的事件类型因此官方无法提供一份穷举清单。最可靠的做法是——在真实会话中实际调用一次events端点直接检查返回的响应即可看到该 driver 在当前环境下暴露的全部事件类型。四、数据能用来做什么拿到events数据后可以做很多有价值的分析计算事件之间的时间间隔例如newSessionStarted减去newSessionRequested得到建会话耗时quitSessionFinished减去quitSessionRequested得到退出会话耗时还原严格的事件时间线把所有事件的时间戳按时间排序还原整个会话从创建到销毁的完整时间轴统计某类命令的平均耗时对commands数组按cmd分组计算endTime - startTime的均值、最大值、最小值找出性能瓶颈命令。例如一段简单的伪代码思路具体语言以你的测试框架为准// 按命令名聚合耗时统计 const timings {}; for (const c of events.commands) { const duration c.endTime - c.startTime; timings[c.cmd] timings[c.cmd] || []; timings[c.cmd].push(duration); } // 之后即可计算各命令的平均耗时、P95 等指标五、添加自定义事件Custom Event除了内置事件Appium 还允许你自定义事件并将其纳入事件时序数据通过Log Custom Event APIPOST /session/:sessionId/appium/log_event向 Appium 服务器发送一个自定义事件名服务器会为其记录当前时间戳之后通过Get Log Events APIPOST /session/:sessionId/appium/events即可取回这些命名事件的时间戳。自定义事件在时序数据中同样以事件类型名 → 时间戳数组的形式出现事件类型名为vendor:event的拼接格式。底层实现事件如何被存储在 packages/base-driver/lib/basedriver/commands/event.ts 中可以看到两个核心方法的实现async logCustomEventC extends Constraints(this: BaseDriverC, vendor: string, event: string): Promisevoid { this.logEvent(${vendor}:${event}); }, async getLogEventsC extends Constraints( this: BaseDriverC, type: string | string[], ): PromisePartialEventHistory { if (util.isEmpty(type)) { return this.eventHistory; } const typeList Array.isArray(type) ? type : [type]; return Object.entries(this.eventHistory).reducePartialEventHistory((acc, [eventType, eventTimes]) { if (typeList.includes(eventType)) { acc[eventType] eventTimes; } return acc; }, {}); },要点logCustomEvent将vendor与event以冒号拼接成vendor:event后写入事件历史vendor 前缀用于命名空间隔离避免不同工具/厂商的自定义事件互相冲突getLogEvents支持按type过滤不传或传空值返回全部事件传字符串返回单个事件类型传数组可同时过滤多个事件类型精确匹配事件类型名不是子串匹配。路由定义这两个 API 在协议层面对应的路由定义位于 packages/base-driver/lib/protocol/routes/appium.ts/session/:sessionId/appium/events: { POST: {command: getLogEvents, payloadParams: {optional: [type]}}, }, /session/:sessionId/appium/log_event: { POST: { command: logCustomEvent, payloadParams: {required: [vendor, event]}, }, },可以看出vendor与event是logCustomEvent的必填参数而getLogEvents的type为可选参数。六、源码级佐证事件与命令耗时如何被记录命令时序的采集点在 packages/base-driver/lib/basedriver/driver.ts 的executeCommand主流程中可以看到命令开始执行前先取const startTime Date.now();第 88 行命令执行完成后再取const endTime Date.now();第 173 行将{cmd, startTime, endTime}压入this._eventHistory.commands数组第 179 行。也就是说每条 Appium 内部命令的startTime/endTime覆盖的是从命令进入驱动执行队列到处理完成的完整耗时这与events响应中commands数组的元素结构一一对应。类型定义见 packages/types/lib/commands/basedriver.tsexport interface EventHistory { commands: EventHistoryCommand[]; [key: string]: any; } export interface EventHistoryCommand { cmd: string; startTime: number; endTime: number; }会话事件在命令前后被写入同样在executeCommand中命令进入前若为createSession则记录newSessionRequested若为删除会话则记录quitSessionRequesteddriver.ts命令完成后若为createSession则记录newSessionStarted若为删除会话则记录quitSessionFinisheddriver.ts。另外当appium:eventTimings开启时GET /session/:id的会话信息响应中还会附带events字段见 driver.ts这是历史行为官方在事件时序文档event-timing.md中特别提示过去事件数据曾作为GET /session/:id响应的一部分返回现在已不再这样做请统一通过 events 端点获取。测试验证事件相关逻辑有完善的单元测试覆盖见 packages/base-driver/test/unit/basedriver/commands/event.spec.tslogCustomEvent(myorg, myevent)后事件历史中会出现myorg:myevent键第 12-13 行印证了vendor:event的存储格式getLogEvents()不带参数返回全部事件包括内置事件与自定义事件第 15-22 行getLogEvents(testCommand)只返回匹配该类型的事件第 36-43 行传入数组[testCommand, testCommand2]可同时过滤多个类型第 52-61 行过滤是精确匹配传入testCommandDummy不会匹配到testCommand第 45-50 行传入不存在的noEventName返回空对象第 72-77 行。这些测试用例同时给出了getLogEvents过滤语义的权威说明类型名必须精确匹配全等既不是前缀也不是子串匹配。七、配套工具Appium 官方事件解析器Appium 团队维护了一个事件时序解析工具event timings parser可直接用于从事件时序输出中生成各类报告appium/appium-event-parser详见 event-timing.md。使用思路是先把events端点返回的原始时序数据保存下来再交给该解析器分析。它适合批量产出报表的场景例如将多条测试运行的事件时序汇总成统计报告用于定位哪条命令最耗时哪次会话启动最慢等。八、实战建议与小结综合以上内容给出几条落地建议开启方式创建会话时设置appium:eventTimings: true新项目更推荐直接使用getLogEvents端点因为 capability 已被标记为 deprecated采集时机在quit之前调用 events 端点以获取整个会话的完整事件与命令时序分析维度事件时间戳数组用于分析会话生命周期各阶段耗时commands数组用于统计各类命令的平均耗时与异常耗时定位性能瓶颈自定义埋点在关键业务步骤前后调用logCustomEvent(vendor, event)把业务语义如登录完成数据加载完成写入时序便于结合命令耗时还原完整的用户路径时间线批量报表需要持续产出性能报告时使用官方 event-parser 工具处理导出的时序数据。事件时序功能让 Appium 从只能断言功能是否通过升级为还能量化每一步花了多久是性能分析、CI 稳定性排查与容量评估中非常实用的能力。【免费下载链接】appiumCross-platform automation framework for all kinds of apps, built on top of the W3C WebDriver protocol项目地址: https://gitcode.com/GitHub_Trending/ap/appium创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考