如果只做过纯 Native 或纯 Flutter 项目,很容易低估混合开发 App 的测试复杂度
但只要项目里同时存在 Native 页面、WebView、Flutter 或其他跨端方案,测试这件事就会迅速变得不那么“干净”。

我第一次明显感受到这一点,是在一个 iOS 项目里:
部分核心页面是 Native,活动页用 H5,另一些模块是 Flutter。
功能测试能过,但问题总是在一些“说不清楚”的场景里冒出来。


混合开发的问题,很少只属于某一层

测试反馈往往是这样的:

  • 页面偶发白屏
  • 某些设备上明显卡顿
  • 返回上一级时短暂无响应
  • 用久了之后整体变慢

当你去问“是 Native 的问题还是 Web 的问题”,
很多时候,答案是:都不是单独一边的问题


只用某一种测试方式,结论往往不完整

最早我们还是按老习惯来测试:

  • Xcode 跑功能
  • Instruments 看性能
  • Safari Inspector 看 H5

每个工具都能看到一些东西,但总感觉拼不成一条完整的线。

比如:

  • Safari Inspector 显示 JS 正常
  • Native 内存看起来也不高
  • 但实际体验就是越来越卡

这类问题,如果不把不同层的行为放在一起看,很难解释。


混合 App 测试,第一步是“统一视角”

后来我意识到,混合开发的 App 在 iOS 上,最终都要接受系统调度。
不管页面是 Native、Web 还是 Flutter,它们的 CPU、内存、线程、能耗,最终都会体现在同一个进程里。

从这个角度看,把 App 当成一个整体来测试,反而更有意义。


从 Instruments 开始,但不要停在这里

Instruments 仍然是一个非常重要的工具。

通过 Time Profiler,可以看到:

  • 渲染压力集中在哪一段
  • WebKit 线程何时活跃
  • Flutter Engine 是否频繁调度

这些信息能帮你判断“压力来源在哪里”,
但它不太适合回答“问题什么时候开始出现”。


当问题是“跑久了才有”,测试方式就得变

在一个版本回归中,我们发现:

  • 第一次进入混合页面一切正常
  • 多次切换后开始掉帧
  • 再回到 Native 页面,响应也变慢

这个现象,用 Instruments 很难一次性抓住。

于是我开始在真机上跑完整使用流程,并引入 克魔(KeyMob) 做持续观察。


KeyMob 在混合 App 测试里的作用

我并没有把 KeyMob 当成“某一层的工具”,而是把它当成一个观察窗口:

  • 看 CPU、内存是否随着页面切换逐步上升
  • 看 FPS 在混合页面前后是否发生变化
  • 看网络、日志是否在后台持续活跃

当你把 Native、WebView、Flutter 页面来回切换跑一遍,再回头看这些数据,很多问题会变得非常直观。


Web 层的测试,不能只看 Web 层

在混合项目中,Web 页面经常被单独测试。
但在 App 里运行时,它的行为会受到更多限制。

我一般会同时使用:

  • Safari Inspector:确认 JS 行为、定时任务、资源加载
  • KeyMob:观察 Web 页面前后整体内存与 CPU 变化

在一次排查中,我们发现 Web 页面本身没有明显问题,但在退出后短时间内仍然占用资源,导致后续 Native 页面受影响。


网络问题,往往在混合场景下被放大

通过 Charles 抓包,可以看到一些在功能测试中不明显的情况:

  • 某些接口在 H5 页面和 Native 页面都会调用
  • 切换页面时请求被重复触发
  • 弱网下重试逻辑叠加

这些行为单独看并不严重,但放在混合场景中,就容易变成性能问题。


混合 App 测试,本质是跨层对照

慢慢地,我形成了一种比较稳定的测试方式:

  • 用 Instruments 看压力来源
  • 用 Safari Inspector 理解 Web 行为
  • 用 Charles 对照网络
  • 用 KeyMob 把所有行为放在同一条时间线上看

这样做的好处是:
你不需要一开始就判断“是哪一层的问题”,而是让数据自己说明问题