设备覆盖:不是测所有型号,而是测风险区域
IoT 与 ESP32 类的 APP 有一个显著特征:硬件端不是标准化的手机,而是一块烧录了固件的开发板或定制 PCB。APP 通过 Wi-Fi、BLE 或 MQTT 与设备通信,因此测试必须覆盖“APP 版本 × 固件版本 × 通信协议版本”的组合,而非单纯罗列手机型号。
企业常犯的错误是盲目采购几十台不同手机做兼容性测试,但真正导致线上事故的往往是固件与 APP 的协议不匹配。例如,ESP32 的 BLE 服务 UUID 在固件升级后发生变化,但旧版 APP 仍按旧 UUID 扫描,导致设备发现失败。
设备覆盖测试的核心原则是“风险驱动”。具体做法如下:
- 固件版本组合优先:列出所有在产 ESP32 固件版本(包括已停产的旧版本,只要仍有设备在线)。每个固件版本至少有一台真实设备参与测试。
- 通信协议差异:区分 Wi-Fi、BLE、MQTT 三种通信方式。不同通信方式下的超时、重连、数据包丢失处理逻辑差异很大,必须单独覆盖。
- 网络环境模拟:在弱网(信号强度 -80dBm 以下)、高延迟(500ms 以上)、丢包(10%)环境下测试 APP 行为。ESP32 的 Wi-Fi 模块在弱网下容易断开,APP 必须能自动重连并恢复状态。
- 手机系统版本:覆盖 Android 10 以上和 iOS 12 以上的主流版本,重点测试系统权限变更(如 Android 13 的附近设备权限、iOS 14 的本地网络权限)对蓝牙扫描的影响。
- 低端设备:选择运行内存 2GB 以下的手机测试 APP 后台存活能力。很多 IoT APP 在后台被系统杀死后无法恢复与 ESP32 的连接,这属于严重缺陷。
设备覆盖的落地方法不是采购大量实体机,而是建立“核心设备池 + 云真机补充”的模式。核心设备池包含 5 到 8 台不同固件版本的真实 ESP32 开发板或产品样机,日常回归测试必须全部跑通。云真机用于覆盖手机型号和系统版本差异,但无法替代真实硬件测试,因为云真机无法连接物理 ESP32。
对于 BLE 通信,还需要考虑多设备并发场景。测试时至少准备 3 台 ESP32 同时广播,验证 APP 的扫描列表刷新、设备过滤和连接切换是否正常。这是很多团队漏掉的测试点,但实际用户家里可能有多个 IoT 设备同时运行。
自动化测试:分层策略与工具选择
IoT APP 的自动化测试比普通 APP 复杂,因为测试对象不仅包括 APP 界面,还包括 APP 与 ESP32 的通信链路。分层策略可以降低维护成本:
- 单元测试:覆盖 APP 端的协议解析、数据校验、状态机转换。例如,解析 ESP32 返回的 JSON 格式传感器数据时,对异常数据(缺失字段、类型错误)的处理逻辑必须有单元测试。iOS 使用 XCTest,Android 使用 JUnit 或 Mockito。
- 集成测试:模拟 ESP32 作为 BLE 外设或 MQTT 服务端,验证 APP 的通信流程。iOS 可使用 CoreBluetooth 的模拟外设 API,Android 使用 BluetoothLeScanner 的模拟扫描。MQTT 部分可以搭建本地 Mosquitto Broker,用脚本模拟设备上报数据。
- UI 自动化:使用 Appium 或 XCTest UI 测试框架,覆盖核心用户路径,如设备配网、控制面板操作、固件升级触发。注意,UI 自动化必须与集成测试配合,不能单独依赖模拟数据。因为很多 UI 缺陷源于真实设备的数据格式与模拟数据不一致。
- 端到端测试:用真实 ESP32 设备运行自动化脚本。这需要搭建一个硬件测试床,包含电源控制、串口日志采集和远程重启功能。每次构建后自动触发端到端测试,验证 APP 能否成功连接 ESP32、发送指令并接收正确响应。
工具选择上,推荐以下组合:
- 单元测试与集成测试:XCTest(iOS)、JUnit + Mockito(Android)
- UI 自动化:Appium(跨平台)、XCTest UI(iOS 专属)
- 端到端测试:自研框架,基于 Python 或 Node.js,通过串口与 ESP32 通信,同时使用 ADB 或 XCUITest 控制 APP
- CI/CD 集成:Jenkins 或 GitHub Actions,在每次 PR 合并前运行全部自动化测试
自动化测试的覆盖率目标:单元测试不低于 80%,集成测试覆盖所有协议版本组合,端到端测试覆盖核心配网流程和至少 3 个控制指令。不要追求 100% 的 UI 自动化覆盖,因为 IoT APP 的界面变动频繁,维护成本过高。
一个常见陷阱是过度依赖模拟器。iOS 模拟器不支持 BLE,Android 模拟器对 BLE 的支持有限,因此集成测试必须在真机上运行。建议在 CI 流水线中接入真机集群,例如使用 AWS Device Farm 或自建 Android 测试机柜。
灰度发布:分阶段、分用户、分设备
IoT APP 的灰度发布不能照搬普通 APP 的按地区或按比例分发的模式,因为 APP 与设备的绑定关系使得回滚成本更高。一旦用户升级了 APP 但固件不兼容,设备可能完全不可用,而用户无法自行降级 APP。
推荐的灰度发布策略分为三个阶段:
- 第一阶段:内部测试。发布给团队内部和 10 到 20 名种子用户,这些用户必须拥有不同固件版本的 ESP32 设备。种子用户需要签署知情协议,并愿意在出现问题时配合提供日志。这一阶段持续 24 到 48 小时。
- 第二阶段:按设备固件版本灰度。在 APP 后台配置白名单,只允许固件版本在 v2.1.0 以上的设备对应的用户升级。这样做的好处是,如果新 APP 修改了通信协议,旧固件设备不会受到影响。灰度比例从 5% 开始,逐步提升至 20%、50%,每个比例持续至少 24 小时,观察崩溃率和用户反馈。
- 第三阶段:全量发布。当第二阶段数据稳定后,开放至 100%。但即使全量发布,后台仍需保留紧急下架开关,以便在发现严重问题后立即停止新用户下载,同时触发回滚。
灰度发布的技术实现要点:
- APP 端需要内置版本检查和灰度开关。开关可以从远程配置服务(如 Firebase Remote Config 或自建配置中心)拉取。开关值包括“是否允许升级”“目标固件版本范围”“灰度比例”。
- 对于 iOS,TestFlight 天然支持外部测试,但无法按固件版本过滤。因此需要在 APP 启动时通过远程配置判断当前用户是否符合灰度条件,不符合则隐藏升级入口。
- 对于 Android,可以使用 Google Play 的 staged rollout(按比例分发),但同样需要 APP 端二次校验固件版本兼容性。Google Play 的 staged rollout 无法按设备属性过滤,只能按用户比例。
灰度发布期间必须监控的指标:
- 崩溃率:以 0.1% 为阈值,超过则暂停灰度。
- 配网成功率:灰度版本与基线版本对比,下降超过 2% 则暂停。
- 用户主动降级率:如果用户在灰度期间卸载并安装旧版本,说明体验问题严重,需要立即分析。
- 设备离线率:灰度版本上线后,已连接设备突然离线的比例。这通常是协议不兼容的信号。
在 SystemDo 过往的 IoT 项目中,我们曾遇到灰度发布时忽略设备固件版本过滤,导致一批固件版本 v1.8.0 的设备在升级 APP 后无法连接。事后复盘发现,v1.8.0 固件使用的 BLE 服务 UUID 在新版 APP 中被废弃,但灰度开关只按用户比例分配,没有关联设备固件。此后我们强制要求所有 IoT APP 的灰度发布必须关联设备固件版本。
崩溃监控:从日志到根因
IoT APP 的崩溃监控不能只依赖第三方 SDK(如 Crashlytics、Sentry),因为这些 SDK 只能捕获 APP 侧的崩溃,无法反映通信层的问题。很多“看起来像崩溃”的问题实际上是 APP 与 ESP32 通信超时后触发的保护性重启,这在日志中表现为非正常退出,但第三方 SDK 可能无法正确归类。
崩溃监控体系应包含三层:
- 第一层:APP 侧崩溃采集。使用 Crashlytics(iOS/Android)或 Sentry,自动捕获未处理异常、ANR(Android)、卡顿(iOS)。每个崩溃事件必须附带设备固件版本、通信方式、当前操作步骤。
- 第二层:通信层错误采集。在 APP 的网络层和 BLE 层埋点,记录每次连接、断开、超时、数据校验失败的事件。这些事件不一定是崩溃,但可能预示协议不兼容或固件异常。推荐使用自建日志系统,将日志上传到后端,按设备 ID 和固件版本聚合分析。
- 第三层:用户反馈与日志上传。在 APP 中提供“发送诊断日志”功能,用户遇到问题后可主动上传。日志应包含 APP 版本、固件版本、最近 30 秒的通信日志和操作轨迹。这一层能捕获第三方 SDK 无法覆盖的“非崩溃但体验异常”的问题。
根因分析的常用方法:
- 崩溃堆栈 + 设备上下文:如果崩溃发生在 BLE 回调中,检查堆栈中是否涉及 CoreBluetooth 或 Android BluetoothGatt 的异常。同时查看设备固件版本,确认是否是新固件引入了不兼容的广播数据格式。
- 通信日志时间线:将 APP 端的通信日志与 ESP32 的串口日志对齐。如果 APP 发送了“配网指令”但 ESP32 没有响应,说明可能是 MQTT 主题错误或 ESP32 的 Wi-Fi 模块异常。
- 版本差异对比:对比正常版本与异常版本的通信协议文档,重点关注 UUID、特征值类型、数据长度、校验和算法是否有变化。
崩溃监控的告警阈值设置:
- 实时告警:崩溃率超过 0.5% 时触发 P0 告警,需要立即响应。
- 趋势告警:某个固件版本的崩溃率在 1 小时内上升超过 0.3% 时触发 P1 告警。
- 通信错误率:某个操作(如设备配网)的失败率超过 5% 时触发 P1 告警。
注意,崩溃率不能只看整体数字,必须拆解到“固件版本 × APP 版本”的维度。整体崩溃率可能是 0.1%,但某个旧固件版本的崩溃率可能是 5%,这属于严重问题,但容易被整体数据掩盖。
回滚方案:APP 回滚与固件回滚的联动
IoT 场景下的回滚比普通 APP 复杂,因为状态由 APP 和 ESP32 共同维护。如果只回滚 APP 而不回滚固件,可能出现“旧 APP 无法理解新固件的广播数据”的问题。因此,回滚方案必须设计为联动机制。
APP 回滚的三种方式:
- iOS:通过 App Store Connect 下架当前版本,用户只能下载旧版本。但这个过程有延迟,且用户必须手动操作。更可靠的方式是使用远程配置强制旧版本 APP 重新激活,但 iOS 不允许强制降级安装在已升级的设备上。因此,iOS 的最佳实践是在灰度阶段就发现问题,避免全量发布。
- Android:通过 Google Play 下架当前版本,同时将旧版本重新标记为“可用”。用户可以在 Play 商店的“已安装应用”列表中找到旧版本并重新安装。对于企业分发,可以直接推送旧版本 APK 并提示用户安装。
- 自建分发渠道:对于非公开分发的 IoT APP(如企业定制版),可以维护一个版本列表,在 APP 启动时检查远程配置,如果发现当前版本被标记为“不兼容”,则自动下载并安装旧版本。这种方式灵活性最高,但需要注意签名和安装权限问题。
固件回滚的触发条件:
- 如果 APP 回滚后,设备仍然无法正常工作,说明问题出在固件侧,需要触发固件回滚。
- 固件回滚通常通过 OTA 实现。ESP32 支持两个固件分区(OTA_0 和 OTA_1),回滚时 APP 向设备发送回滚指令,设备切换到备份分区并重启。
- 固件回滚的触发时机:在 APP 启动时检测到设备固件版本与 APP 不兼容,且 APP 自身版本已被标记为“待回滚”,则自动向设备发送回滚指令。这个过程需要用户确认,因为回滚期间设备会离线 10 到 30 秒。
回滚决策的自动化原则:
- 当 APP 的崩溃率超过 2% 且与某个固件版本强相关时,自动触发该固件版本的回滚。
- 当通信错误率超过 10% 且持续 30 分钟时,自动暂停该版本 APP 的全量发布,并通知运维团队。
- 回滚决策不能完全自动化,必须有人工确认环节。自动化系统生成回滚建议,由运维负责人确认后执行。
回滚演练:每个季度至少进行一次回滚演练,模拟 APP 发布后出现严重兼容性问题,测试团队需要在一个小时内完成回滚决策、执行和验证。演练中必须包含真实 ESP32 设备,验证回滚后 APP 与设备的通信是否恢复。
一个容易被忽视的细节是回滚后的数据一致性。APP 回滚后,本地存储的配置数据(如 Wi-Fi 密码、设备别名)可能与新固件不兼容。因此,回滚脚本必须同时清除或转换本地数据,避免旧 APP 读取到无法解析的配置项。
发布流程中的风险管理
IoT APP 的发布流程不能只靠技术手段,还需要配套的流程管控。以下是基于工程实践总结的风险管理要点:
- 发布前 checklist:包括所有固件版本的兼容性测试通过、灰度开关配置正确、远程配置服务正常、崩溃监控告警通道可用、回滚方案已就绪。每个检查项必须有人签字确认。
- 发布窗口:选择用户活跃度最低的时间段,例如凌晨 2 点到 5 点。对于全球用户,需要按时区分批发布,避免所有用户同时升级。
- 发布暂停条件:一旦崩溃率超过 0.5% 或配网失败率超过 3%,立即暂停发布。暂停后由技术负责人评估是否回滚或修复后继续。
- 发布后监测期:全量发布后的 72 小时内,每 4 小时出具一份质量报告,包含崩溃率、配网成功率、设备在线率、用户反馈数量。监测期内不发布新的固件版本,避免干扰。
- 版本锁定:对于企业级 IoT 项目,建议在 APP 中实现“强制升级”功能。如果用户长时间不升级,且当前版本存在已知安全漏洞,APP 应在启动时弹窗提示升级,并在一段时间后强制升级。强制升级前必须确保兼容性测试通过。
风险管理的核心是“可观测、可控制、可回滚”。可观测指崩溃监控和日志系统必须实时可用;可控制指灰度开关和远程配置必须能快速生效;可回滚指 APP 和固件的回滚方案必须经过演练验证。
在实际项目中,很多团队把精力放在功能开发上,忽视了发布流程的建设。结果往往是第一次发布就出现严重问题,然后紧急回滚,用户信任度受损。如果能在项目初期就投入资源建设测试矩阵、自动化测试、灰度发布和回滚机制,后续的迭代会顺畅得多。