这是鼎叔的第一百三十九篇原创文章。行业大牛和刚毕业的小白,都可以进来聊聊。
欢迎关注公众号《敏捷测试转型》,星标收藏,大量原创思考文章陆续推出。本人专著《无测试组织 - 测试团队的敏捷转型》已出版(机械工业出版社)。
从十五年前讲起

这个系列是技术考古文,从移动互联网初期讲起: APP 专项性能评测的前世今生。
时钟拨回十五年前,那时还没有字节跳动,没有滴滴,鹅厂的微信也没有发布,iPhone 和 Android 移动平台开始在国内爆发的一年。
这个主题的前半部分内容来自我做的早期课程,背景是当年鹅厂要给全体技术人员进行移动互联网技术的普及(扫盲),以便(强制)让集团的员工们跑步进入移动新时代,我认领了其中关于移动 APP 评测体系的课程。
后半部分会从不同性能指标专项来分头讲解历史上的经典案例,提纯分析和优化的诀窍。最后是相关工具平台的回顾和评价。

最让我感触(惊讶)的一点,就是移动互联网的专项性能技术体系,在过去十几年都没有本质的变化,除了工具能力有点不同,思路打法几乎一模一样。一个课程可以用上十几年,让人产生一种 “今夕是何年” 的恍惚。
不禁感慨,不同时期不同业务的 APP 工程师们,打的都是各种换皮怪。在成熟的技术行业,要专心研究的是历史,而不只是眼前那一亩三分地。

那么本文开篇先要聊清楚:相较于传统桌面互联网,移动互联网 APP 有什么特殊之处?
部分观点来自我和廖叔的探讨。

移动互联网 PK 桌面互联网
我们来回顾下移动互联网的初期遇到了哪些问题,它们是如何导致 APP 性能专项测试愈发重要的。
一 各种网络制式
早期移动网络从 2G 发展到 3G,后来又到 4G 和 5G,移动运营商的基础设施也经历了不断完善的过程。
因为三大运营商的分而治之,连通问题频发,移动互联网爆发期的体验是难以保障的。
对于质量保障团队,我们需要关心不同代际设备的互通质量,也要关心不同运营商的不同网络接入点覆盖。当年有的网络接入点为 WAP 网站专供,有的则是访问和桌面互联网一样的 WEB 网站。
这些痛点要素组合起来,就带来巨大的测试覆盖压力,远高于传统互联网的测试量。
基建水平和运营商的调控策略,就会导致手机信号的水平参差,最常见的吐槽场景就是弱信号/丢包或延迟,以及信道拥塞。

二 流量和电量的限制
移动互联网流量收费模式和有线互联网不同,尤其早期移动上网费用昂贵,用户对于流量变化是非常在意的,流量测试的重要性不言而喻。
电量更是移动设备的长期痛点,强大的服务能力带来难以预测的资源消耗,耗电类 BUG 对软件用户是非常劝退的。

三 需求变更频繁
初期的移动 APP 功能比较单一,体积相对小,迭代周期很短,大家为了先抢占市场,会尽快交付产品。APP 的 UI 也经常在变,竞品之间也抄来抄去。
对于平台型 APP,它的接口复杂度非常高,包含了大量的对外接口,比如微信,手机 QQ,浏览器等软件。激烈的行业竞争导致测试的周期非常短,倒逼我们要进行轻快的品质测试。

四 终端的碎片化
当年 Android 平台是一个野蛮的群雄逐鹿时代,碎片化触目惊心,每个手机厂家都有自己的各种机型和定制 ROM,那时行业也缺乏成熟统一的兼容规范,造成千奇百怪的使用问题。

具有中国特色的双卡双待,在早期也是缺陷重灾区,尤其是通话类、短信类和读写通讯录类功能。
层出不穷的创新硬件,也会增加适配工作量,如多年前的水滴屏/留海屏,这些年流行的折叠屏,都是必须要专项适配的,类似还有各种前置后置摄像头,在 APP 拍照功能中需要考虑。
手机内置的各种新鲜传感器,在特定功能(如摇一摇,抬手触发等)中需要针对机型适配,以免调用失败。
十几年前,一款 APP 如果不做机型适配测试和修复,根本不敢上市,据说有 70% 的概率会出现致命的适配缺陷。
当然这些年要好些,毕竟安卓系统很成熟了。不过每年的最新硬件还是要特别关注。

五 “移动” 带来的体验短板
前文提到,移动环境下的耗电和流量是新出现的大问题。
正因为用户自己也在 “移动”,速度叠加出了很多环境有关的问题,如在高铁上上网,或是走入人群密集的地方上网,又或是经历不同运营商的网络切换,都会带来意想不到的网络问题。

六 激烈的竞品对比
在资本和行业的巨头抢占市场时,功能差异保持不了多久(早期的软件商很难形成独有功能壁垒),大家都面临一个同质化竞争时代,真正拉开差距的其实是性能表现。
每一个兵家必争之地都有两个以上的巨头平台在激烈竞争,能不能提供稳定顺滑的服务就是竞争关键,如果安装包小一些,耗电少一点,长时间运行不崩溃,就能切实提高用户粘性。

为了每个版本例行做性能测试和竞品对比,团队投入了大量精力建设竞品对比自动化平台。
基于这个背景,专项性能测试和调优就成为一个重要的新科目,从事 APP 开发和测试同学也逐渐把这款技能作为核心技能。

专项性能指标

能成为默认的性能评测指标,都是有迹可循的历史逻辑,匹配当时的现实痛点。

每一个专项测试的工具实用,观察方法,优化手段,都积累了大量干货。基本的分析逻辑到今天也没有本质的变化,导致性能问题的原因也逃不出那几点。
行业有一些新的工具,可以根据自己习惯去使用,但性能工具使用门槛是很低的,关键是掌握性能消耗的原理,以及选取关键的场景来做测试。

一 Monkey 测试
Monkey 是我最喜欢的稳定性测试专项,可以零成本发现各种功能或性能问题,模拟用户进行各种随机点击。
当年我回答学员一个印象深刻的问题: Monkey 和 Monkey runner 有啥区别?
区别可大了,前者是一个谷歌的命令行工具,搭配各种参数形成指定的点击策略。后者则是 C/S 架构的 UI 自动化工具,和随机测试没关系。
我们给手机下达 ADB 的 monkey 指令,向被测 APP 发送丰富多彩的用户事件,并可以控制不同事件类型的下达比例。
Monkey 一旦采集到 crash 和 ANR 等错误信息就会报错,同时可以选择继续执行随机点击。

大厂团队还开发了创新 Monkey 工具,顺便把从 monkey 测试衍生的很多检测一起做了,比如在长时间稳定测试中关注内存异常。比如阿里开源的 Maxim,腾讯开发的 New Monkey。
Android Monkey 是 Google 为开放生态设计的随机事件生成工具,依赖 Linux 内核的 input 事件注入和 adb 调试接口。而 iOS 的封闭性导致其无法直接复现这一逻辑。
相对 Android 平台,封闭的 iOS 平台上非官方的专项工具就小众很多,可以用 CrashMonkey4Ios,SwiftMonkey。

Monkey 在测试中的注意事项:
首先要找到被测的核心页面,并在核心功能页面稳定地运行几小时。对于 OPPO 这样的手机厂家,对内置 APP 都要进行足够长时间的 monkey 测试,并由专门团队跟进问题闭环,每个版本都能发现十来个稳定性缺陷。
其次,根据用户操作特性选取随机操作类型,比如某些页面的滑动操作比较多,则滑动操作的事件占比就更高。
第三,跳出困住的界面。在没有算法优化的情况下,从一个复杂的页面跳入其他功能界面的概率可能很低,有时自动运行了几个小时还是原地打转。我们通过算法判断在停留过久的页面,自动帮助 APP 跳入指定的新页面(比如识别 “后退”/“退出” 按钮并定时点击),继续进行 Monkey。
第四,给 Monkey 增加截图功能,当算法察觉到性能故障或异常日志时,马上截图,这样便于开发者定位真实场景,大幅提升 debug 效率。

二 内存测试
Android 是基于 Linux 内核,内存管理涉及 Java 堆(Java Heap)、Native 堆(Native Heap)、Dalvik/ART 虚拟机等。
内存测试关注的核心数据有四个:
内存泄漏:对象不再使用但未被 GC 回收(如长生命周期对象持有短生命周期对象引用);
内存溢出:应用占用内存超过系统限制(如 Android 的 heap size 上限,iOS 的 OOM Killer 机制);
内存抖动:短时间内频繁分配/释放内存(如循环中创建临时对象),导致 GC 频繁触发,引发卡顿;
内存峰值:关键操作(如启动、加载大图/视频)时的内存峰值是否超过系统阈值。

以金融 APP 为例,哪些页面操作容易发生内存问题?
我认为调用数据量比较多,且加载频繁的界面容易出内存问题,比如高频交易、资产分析、资产详情页、行情首次调取展示、订单列表页等等场景。
另外,长时间进行计算和运行的功能,也容易发生内存泄露。如挂盘、盯盘功能,在使用过程中关注内存占用曲线,看看是否保持一个逐步上升的趋势。
内存溢出容易导致崩溃和拖慢,但还有一种不容易发觉的内存碎片化问题(分配了大量小对象导致),手机在使用中慢慢地变卡,因为内存碎片没有及时回收。
内存的崩溃有可能导致用户金融数据丢失,这个隐患需要小心对待。

标准化的内存测试流程应该是什么样子?
我认为把握好几点即可:
用纯净的版本,不要用调试组件和调试日志比较多的版本,避免性能干扰,选择从用户层面比较正式的版本。
内存是一个跟时间波动有关联的指标,所以在测试前,测试中,测试后要观察它的曲线变化。
测试场景分为:启动后待机,切换到后台,反复使用核心功能。

发生内存泄露的高概率场景有:
前台展现了大量图片。因为处理回收机制的问题,没有释放足够的图片占用空间。使用位图要特别注意加载后别忘了释放,它占用的空间很大。
大量发生网络传输数据的时候。这时也可能使用了大量缓存却没有很好的回收。
前后台场景切换比较多的时候。从前台到后台可能一些内存没有做比较合适的刷新处理。
软件处理时分配了很多临时变量,包括临时数组,它们被迅速的回收,需要观察是否真正释放了空间。
以上经验也同样适用于服务端性能测试。

大家可以看看古早的一些性能测试页面,如 MAT,以及看 dumpsys 的 memory info。

还有一些官方的工具,通过 Android profile 实时监控内存情况,通过 LeakCanary 去检查内存泄漏。
iOS 这边的内存测试工具就很少,一般以 Xcode 生态为主。Xcode 内置的性能分析套件,其中 Leaks、Allocations、VM Tracker 是内存测试的核心工具。
另外,Meta 公司的 memory profile 工具可以跨平台使用。

最后,内存测试过程中要注意什么?
第一,内存测试不能保证每次都能发现问题,所以通常要测试多次,采集平均值,我们当然尽量自动化采集过程。
第二,尝试分析堆栈,利用 IDE 和 MAT。如 Dalvik heap 堆栈内存使用和其他内存空间的增长是否正常。
第三,有些新功能引入,可能带来固定的新内存空间,我们需要评估这个增长是否在预期之内。有大厂会强制要求新功能带来的增量内存占用不能超过一定比例。所以我们首先要有一轮基准性能测试,才能知道"存量"内存空间占用是多少。
引入新的 API 或 SDK 时,我们要考虑引入的代价。我们要把影响内存的操作放在临时进程,而不是占用主进程的内存空间。
最后一点非常关键,要把一些大型的循环依赖拆掉,把大循环变成小循环,这与电量优化的原理也是类似的。大循环会带来很多资源消耗的风险。

未来,我们继续介绍其他专项性能指标的评测及优化方法。


↙↙↙阅读原文可查看相关链接,并与作者交流