小优有话说:
App Crash就像地雷。
你怕它,想当它不存在。无异于让你的用户去探雷,一旦引爆,用户就没了。
你鼓起勇气去扫雷,它却神龙见首不见尾。
你告诫自己一定开发过程中减少crash,少埋点地雷,但总是不得其法。
降低Crash率,需要的是技巧、工具、耐心与时间。
本文由腾讯天天P图测试团队现身说法,为你讲述他们将Crash率直降90%背后的故事,希望能为你“排雷”提供一些思路。
PS:以后每周四记得关注这里哦!小优将为你精选“干货”,让腾讯的开发&测试大牛们陪你一起为提升产品质量!
———–我是正文分割线———–
特约供稿人:腾讯公司天天P图项目 乔伟康、梁小龙
天天P图作为图像处理类APP,内部集成了很多功能,包括滤镜、人脸检测、美白、磨皮、美妆、拼图、相机等,而且这些功能多是用底层算法依靠GPU实现,如何保证这些功能在众厂商生产的Android手机上正常高效运行,对于测试来说是一项极具挑战的任务。本文主要针对Android天天P图业务介绍我们在降低Crash率方面所做的工作,当然这里也离不开开发同学们的大力支持。
一、Android天天P图业务介绍
1.Android天天P图功能模块数据及潜在测试挑战
Android天天P图内部共9个大模块,共包含近200个子功能,其中各模块内部子功能数目如下表所示:
其中:
1)美化照片的滤镜/马赛克,美容美妆的磨皮/美白/上妆等功能均需要用到Native层的算法库,具有爆发底层SIGSEGV、SEGABRT等算法类crash问题的风险,但此类问题很难在单一手机上通过测试数量有限的图片发现,需要考虑如何在有限的机型及时间的情况下,尽可能的暴露此类问题;
2)自拍相机模块由于需要适配各种定制的Android的平台设备,同样存在兼容性Crash的爆发风险,自拍相机的兼容性问题对测试人员来说是一项很具挑战的任务;
3)Android天天P图作为图像处理类软件,美容美妆/故事拼图等模块对内存的要求较为苛刻,需要预防OOM的风险,测试需要知道如何判断鉴别P图内存使用效率及释放时机是否高效合理;
2.Android天天P图各版本Crash率趋势图
面对如上风险,我们再来看一下Android各版本的Crash率情况
从上表可以看出,产品问世初期的两个版本Crash率较高,但在随后的版本中,在不断增加新功能的同时,Android天天P图的Crash率整体呈下降趋势,由最初版本发布的2.2%降低到目前的0.24%。
(小优注:因数据涉及产品隐私,小优打了马赛克,还请各位看官见谅)
二、问题分析与解决策略制定
1.问题分析
1)存量问题分析
Android天天P图接入了RDM(小优注:腾讯内部研发管理工具)异常上报和Bugly(小优注:腾讯bugly为产品质量监控平台)异常上报用于观察外网用户遇到的Crash问题,这里先看下版本发布初期上报的主要问题:
下图为V2.0版本上报的TOP10 Crash问题
从上图可以可到,Crash主要分如下几类:
–lRuntimeException
–lSIGSEGV/SIGBUS
–lNullPointerException(NPE)
其中RuntimeException需要具体问题具体分析,SIGSEGV/SIGBUS类的问题属于底层算法类问题,NullPointer类的问题为代码保护不规范类问题。
2)新增问题预防
为了降低Crash率,不只是要解决存量遗留的问题,同时需要关注新增问题的预防,保证无突出严重的新增Crash问题。
2.策略制定
1)分层测试
天天P图内部的很多功能使用底层算法库,如人脸识别、五官定位、彩妆上妆等。鉴于图片和环境的多样性,以及在上层无法验证部分异常场景,测试团队将APP Java层和Native层代码分开测试的策略。
针对算法层的测试使得测试更具针对性,覆盖的场景更多,同时使用自动化测试工具也使得在图片多样性覆盖上的测试效率更高。下图为部分P图SDK测试图片集。
2)存量问题解决
天天P图测试团队持续关注每个版本外发后的异常上报,筛选出其中较严重的10-20个Crash问题,分析提单跟进,在下一版本中进行解决,截止当前版本,测试配合开发共修复超过100个Crash问题,有效保证了Crash率的降低。
以下数据为V3.3版本外发后Crash的分布分析,在V3.4版本针对性解决部分Crash问题后,Crash率降低了1个千分点。
从统计数据上看,主要的Crash问题集中在自拍相机模块,测试调整NewMonkey运行参数,针对自拍相机进行集中测试,
3)新增问题预防
由上面的分析,我们确定主要的Crash集中在底层算法、NullPointer和其他Java层异常导致,如何在版本测试初期尽快发现这类问题,我们主要采用Crash问题分析总结反馈补充测试用例、引入改造现有工具等方式完成,具体可参考第三部分;
4)Crash问题分析总结补充测试场景
每一个版本外发后,天天P图测试团队会总结分析前一版本系统测试中及bugly异常上报发现的Crash问题,归类发生场景,分析诱因,最后反补到测试用例中,截止目前,共补充测试用例34条,总结文档11篇。
三、解决方案实施
接入和改造的工具发现Crash问题数目一览表
1.NewMonkey接入改造:让严重Crash无处藏身
1)引入背景
终端产品需要迭代快,而外发前一个阻碍发布的问题是发现严重的Crash问题,在这个时间修改Bug一是可能会引入其他问题,同时也极有可能导致外发延期,测试团队需要考虑如何避免这种问题,保证产品按时高效发布。
2)NewMonkey接入改造
NewMonkey是社交网络质量部测试开发团队以Android Monkey为基础,订制的一个更高效的自动化稳定性测试工具,相较于Monkey,NewMonkey的测试更具有平均性,能够覆盖APP内更多的Activity。NewMonkey运行于测试开发团队维护的NewMonkey Wall上,接入后测试手机会每天定时拉取最新RDM安装包进行测试,这样能够保证今早发现版本中存在的严重Crash问题。
但鉴于天天P图APP内操作的特殊性,天天P图测试团队针对NewMonkey的操作封装了更多适用于天天P图的测试操作,如:涂抹屏幕、滑动滑竿等,同时结合天天P图业务模块使用的分布,通过配置文件修改测试Blacklist文件使得定制后的NewMonkey运行更高效,如下是改造前后NewMonkey发现Crash问题的相关数据:
截止目前,本地运行的NewMonkey共在天天P图项目中发现148个Crash问题,有效的保证了外发版本质量。
(小优注:NewMonkey系腾讯内部研发的测试工具,据小优所知暂不支持接入外部app,但需要相关工具介绍文档的童鞋们可以在公众账号留言哦。
插播广告一则:有需求的童鞋可以试试优测的自动化测试,实现原理一致,效果更优哦 [手动骄傲脸])
2.Coverity/CodeDog为代码保驾护航
1)引入背景
正如第二部分问题分析中介绍的V2.0版本外发后的存量问题分析,版本中存在的Crash类问题有一部分为NullPointerException,根据统计数据,Android天天P图各个版本测试中发现的NullPointerException问题大部分都在10个以上,而NPE的问题可以通过代码静态扫描进行预防。
2)Coverity/CodeDog接入与自动提单实现
代码扫描工具很多,天天P图测试团队采用CodeDog和Coverity进行空指针检查。CodeDog是社交网络质量部开发的一款集成工具,工具内部集成了findBugs,Coverity等工具,目前天天P图接入的CodeDog服务包含findBugs、安装包大小检查等,但鉴于资源限制,不包含Coverity,测试团队单独使用了MIG的Coverity进行扫描,由于MIG的Coverity不支持自动提单,测试团队在前人提供的基础提单脚本上进行了优化去重,配合findBugs发现空指针问题。
Coverity共发现96个NPE问题,各个版本发现的空指针数据如下:
CodeDog共发现163个NPE问题,各个版本发现的问题数据如下:
从数据上看,两个工具各有互补,但观察具体的提单可以发现,两个工具之间有一定交集,即提有重复Bug单,但也存在彼此都未发现的Bug。测试团队对自动提单工具进行了一定优化,减少了重复提单问题。
(小优注:同上,CodeDog系腾讯内部研发的测试工具,需要相关工具介绍文档的童鞋们可以在公众号中留言哦。)
3.LeakCanary 内存泄露检测利器
1)引入背景
天天P图作为图像类处理APP,对内存使用要求很苛刻,大量的图片资源加载导致即便微小的内存泄露也有引发OOM的风险,从异常上报看,版本发布初期OOM问题比较突出。
2)LeakCanary接入
LeakCanary是Square开源发布的一款内存泄露检测工具,接入简单,使用方便,在功能测试中便可以发现APP内隐藏的部分内存泄露问题,开发人员在V2.6版本接入了LeakCanary,目前Canary共发现了27个内存泄露问题。
接入LeakCanary后,Android天天P图的OOM问题呈逐步减少状态,相关数据如下:
LeakCanary问题报告详情:
4.腾讯优测云测试平台 发现机型兼容性问题
1)引入背景
Android平台测试最复杂的便是机型兼容性测试,Android天天P图外发后有时候会遇到个别机型频繁上报Crash,由于本地机型限制,及考虑到测试周期,测试团队需要一个自动化的机型兼容性测试平台/团队来解决这类Crash问题;
2)引入腾讯优测
腾讯优测是(utest.qq.com)一个自动化测试平台,MIG的产品,主要工作原理是维护大量测试机,进行自动化测试。利用优测的自动化测试Android天天P图共发现64个Crash异常问题,版本外发前进行一次测试,可基本保证外发无严重漏测Crash问题。
除了用线上的免费测试功能,去年开始Android天天P图开始使用优测的vip测试服务,每个版本做top50机型的核心遍历测试和一些专项测试。
3)优测云手机
优测线上有一个云手机功能,可以远程操控真机(不是模拟器),用户反馈哪个机型有bug,手里又没有对应机型的话,可以在优测直接在线复现,大部分热门机型都有,对测试同学来说挺实用。
5.Crash问题分析-测试场景补充
借助工具和功能测试发现的Crash问题只是第一步,我们需要从发现的Crash问题中寻找共性及复现场景,进而补充到功能测试用例中,避免此类问题再度发生。
Android天天P图经过近几个版本Crash分析积累,共补充测试用例34条,总结相关文档11篇。
1)举例1-Activity与Animation使用注意事项
问题描述:
天天P图分享界面有一个保存动画,如果在动画未完成时返回主界面,天天P图发生闪退;
问题分析:
为验证Activity的生命周期是否影响Animation的播放,在这里做了一个Demo,Demo里包含两个Activity: Main Activity与Animation Activity。其中Animation Activity由Main Activity呼起,在Animation Activity中点击Button播放动画,通过log形式输出各个阶段的时间戳。部分代码如下:
测试步骤如下:
1)在Main Activity中启动Animation Activity;
2)在Animation Activity中点击Button启动动画;
3)在动画播放期间点击返回按钮返回至Main Activity;
测试结果:
15:47:32.863: V/com.tencent.test(21258):Animation Start
15:47:40.822:V/com.tencent.test(21258):Animation Activity Destroyed.
15:48:02.858:V/com.tencent.test(21258):Animation End
程序代码中设置动画时间为30s,从log可看出实际Animation start与Animation End时间间隔为30s。
测试用例补充:
在动画未完成的时候,返回上一级或进入下一级
2)举例2-Activity数据状态恢复
问题描述:
在使用P图美化完自己的照片后进入分享页面,此时可供用户选择的有如下三种操作
1)一直点返回键直至退出程序;
2)点击右上角按钮回到主页面使用其他功能;
3)使用Home键将进程切换到后台;
问题发生在使用第三种操作,在切换到后台很长一段时间后,再次启动桌面APP时,Android会呈现APP上次退出的Activity界面,也即分享界面,此时,若点击左上角返回按钮,P图并没有返回上一个Activity,而是发生了crash。
问题分析:
首先对比操作1和操作3,两者的不同之处在于操作1是直接退出APP,而操作3是先将APP置于后台一段时间后再尝试退出。所以问题的关键在于APP在被置于后台时,Android对我们的APP做了什么。
Android为提升用户体验,系统通过ActivityManager为每一个APP都维护了一个Activity栈,APP第一次被启动时,Main Activity被压入栈(如P图的主界面),当用户点击使用魔法抠图功能时,图库预览Activity被压入栈,此时若使用返回键,栈顶部的Activity将被弹出并销毁,返回主界面Activity,如下图。在本案例中,我们是在点击返回按钮回到上一层Activity时Crash的,所以首先怀疑的是在构建上一层Activity时失败。
从Activity生命周期以及其基本状态图可知,当APP被切换到后台时,Activity变为Stopped状态,但当用户在后续操作中启动多个占内存较多的APP时,P图会被系统默默杀掉,流程图如下。
这里需要注意的是,Android在后台杀死进程和调用任务管理器杀死进程有很大不同:
l调用任务管理器杀死进程:系统中有关APP上次启动的全部状态被清除,包括Activity栈信息,再次启动APP时进入APP主Activity;
l系统在后台杀死进程:Android首先调用onSaveInstanceState()保存Activity的一些状态信息,再次启动APP时返回到上次切换APP前的Activity;
进程在后台被杀死的场景可以通过使用DDMS中的stop按钮模拟。当使用P图美化完照片进入分享界面后,使用Home切换APP至后台,启动DDMS,选中P图进程使用stop按钮,此时由ActivityManager维护的该APP的Activity栈信息将被全部清空。在此时,若使用返回键,Android将重新调用前一Activity的onCreate()创建该Activity,但在这里必须传递给该函数一个Bundle信息,用来恢复Activity显示内容。而开发在此处未使用Bundle信息来恢复前一Activity,这在大多数情况下都是可以正常工作的,因为多数情况下该Activity可以在栈中被找到,尤其是在内存较大的手机,但当Activity栈被清空时,问题便显现出来。所以问题的根源最终被定位为Activity的onCreate()函数实现。
测试用例补充:
在用户可能直接切换至后台的场景,使用DDMS或Android Studio里的模拟系统杀死进程工具杀掉进程,再次启动APP后使用返回键等操作。
1)举例3-IndexOutOfBounds类问题分析
问题描述:
在使用动效拍最后一个滤镜后,点击切换模式选择,发生闪退。
问题分析:
回想Crash发生时的具体场景,Crash多发生在选择一个滤镜后,再点击模式切换按钮。再参考堆栈信息头部的相关信息,Crash的主要诱因在于索引position位置在39的元素时出错,从这里可以得到一个重要信息:一个包含至少三十个元素的ViewHolder在进行索引时出错,再查看自拍相机里所有的子功能,只有滤镜选择功能模块的元素有42个,因此,将定位范围缩小到滤镜选择子功能。
结合初步缩小的定位范围,查看本版本与之相关的新需求,与之相关的只有在模式切换时需要隐藏滤镜子模块中的两个子Icon,到这里很自然的会想到,由于隐藏两个子Icon导致Index索引发生变更,导致末尾的两个Index失效,这里剩下的就是验证了。
由第一部分得出的推断,是当切换到动效拍模式后,滤镜模块中的子Icon隐藏后会引发Crash,因此,首先我们会选择滤镜模块中位于第39位的滤镜素材,然后切换动效拍模式。为了触发滤镜的索引,需要再次点击模式切换按钮(使用滤镜),此时APP发生Crash,log日志与上报一致,问题得以重现。
测试用例补充:
当ViewHolder内部数据数目发生变化时,应该关注Index更新的问题,针对边界值素材及相关功能进行测试;
三、总结与规划
一切测试工作的开展源于对服务业务的理解,以及对当前业务痛点的把握,分析问题,制定方案,通过接入或开发改造工具帮我们提高效率,解决问题。问题的解决并不是最终的目的,我们需要从问题的解决归纳总结出测试的疏漏点,指导我们继续下一次的测试工作开展。(写得太好了,小优忍不住点个赞!)
Android天天P图业务之后的稳定性测试工作开展依然会立于当前业务痛点,通过解决留存问题,接入和改造成熟的测试工具来预防新问题,不断提高产品质量。
关注腾讯优测微信号(wxutest),每周都有新鲜技术干货奉送哦!