Qtest测试之道 移动 H5 广告的性能自动化测试方案 (续)

360Qtest团队 · November 19, 2018 · Last by simple replied at December 28, 2018 · 1998 hits

书接上文

上篇文章《移动H5广告的性能自动化测试方案》末尾我提到了几个问题,是我们在探索移动端H5性能自动化测试遇到的几个比较棘手的坑:

  1. Tcpdump需要root权限,而我们的任务需要分布式执行,且目前高版本的手机root失败率非常高,而且还存在root被还原的情况 。
  2. JS注入以什么方式进行?
  3. pcap文件的解析对https的请求束手无策,而我们的广告请求都是https的。
  4. 白屏时间是否是标准答案?

本文主要介绍我们的解决思路,以供读者参考,如有问题欢迎大家轻喷。。

一些前端性能的背景知识

在介绍新方案之前,想先扯一些前端的背景知识。

关于移动浏览器

目前手机上的浏览器可谓琳琅满目,譬如360安全浏览器,UC浏览器,QQ浏览器,欧朋浏览器,百度手机浏览器,谷歌浏览器,搜狗手机浏览器,猎豹浏览器等等,给测试同学的适配带来了巨大的工作量。另外最近“原创的红芯浏览器”可谓刷爆了各大朋友圈,在此也想小科普一些浏览器内核的知识。
早期全球仅有四个独立的浏览器内核,分别为微软IE的Trident、网景最初研发后卖给Mozilla基金会并演化成火狐的Gecko、KDE的开源内核Webkit以及Opera(欧朋)的Presto。其中,Presto是历史最悠久的内核。
早期的时候chrome和safari都采用了webkit内核,不过Google发布了chrome浏览器后,将使用的浏览器内核更名为chromium,是webkit开源项目的一个分支,随后中2013年4月3日,谷歌的Chromium Blog发表了一篇博客,表示后续Chromium项目将采用Blink渲染引擎。

Android原生浏览器、苹果的Safari、谷歌的Chrome(Android4.0使用)都是基于Webkit开源内核开发的。

浏览器的核心组件

用户界面: 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户界面。
浏览器引擎: 在用户界面和呈现引擎之间传送指令。
渲染引擎: 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
网络层: 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。
用户界面后端: 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
JavaScript 解释器: 用于解析和执行 JavaScript 代码。
数据存储: 这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。

  • 以WebKit内核为例,看一下浏览器渲染的过程

  • W3C performance API
    前文有提到影响用户体验的三个性能指标分别是:

    • Start Render
    • DOM Ready
    • Page Load

这几个关键时间节点,可以通过W3C performance API中的Navigation Timing API来获得。

W3C组织在2010年成立了web性能工作组,专门开发了一个支持将浏览器暴露给JavaScript的API,该API为浏览器开发人员提供了一种精确度量和分析web页面性能的有效手段。整套performance API有10种

  • Navigation Timing: 能够帮助网站开发者检测真实用户数据(RUM),例如带宽、延迟或主页的整体页面加载时间。
  • Resource Timing: 对单个资源的计时,主要统计比如DNS、TCP链接、wating等具体的耗时。

我们需要统计的时间指标可以通过以下公式换算出来:

DNS查询耗时 domainLookupEnd - domainLookupStart
TCP链接耗时 connectEnd - connectStart
request请求耗时 responseEnd - responseStart
解析dom树耗时 domComplete - domInteractive
白屏时间 responseStart - navigationStart
DOMReady时间 domContentLoadedEventEnd - navigationStart
onload时间 loadEventEnd - navigationStart
关于白屏时间的争议

负责性能数据监控的同学在开发过程中发现,白屏数据按照上面时间点抓取存在问题,不太准确,有的时候拿到的截图已经开始有dom数据加载了,为此还发生过争论,带着这个问题仔细的考证了一下,从W3C开源项目的issue列表中看到了项目成员的解答:

问:为什么没把first paint time 写进标准里呢?希望您能把他加进去。
答:因为不能准确定义它,这个时间标准是模糊的。比如一些网页,他本来就是空白的啊,怎么来定义这个paint的时间呢?而且很多人并不在乎,什么时候画出第一个像素,他们想跟踪一下具体某个元素是什么时候画出来的。但是每个网站都不一样,我们不可能来定义太细的接口。所以不能加。Frame Timing 可以让你跟踪画出来的时间,如果你真想追踪first paint(白屏) time,你得等我们有一些 Houdini pieces(巧妙的规避办法 -牛津大字典) 才能有比较好的解决办法。

问:那为什么DevTools里面有呢?为什么不加个时间戳(hiddenvisible)? 我就是想知道,从点击进去到看到内容,用户等了多久,这个值是模糊的么?
答:在DevTools里有,并不是意味着他是一个好的标准。如果想加时间戳来记录,你自己加就行了呗。我只是认为,first paint 不够分量来写进标准,frame Timing 应该更有意义。但是需要时间。

如果读者有不错的白屏时间计算方法,欢迎做文章下面留言。

移动端H5性能测试方案比较

以下几种方案是我们内部讨论过程中梳理过的几种可能性,有些方案可能不适合我们当前的需求,但是适合读者自身,所以观点仅仅给大家作为参考。

1. Fiddler/Charles进行性能抓包
  • 优点:测试人员熟悉功能,上手快,UI功能完善友好,可以通过script扩展
  • 缺点:性能自动化方案实施成本比较高,跨平台支持比较差
2. PhantomJS抓包
  • 优点:PhantomJS(http://phantomjs.org/ ;)是一个可编程的无界面浏览器。它通过JavaScript和CoffeeScript控制webkit的各个模块,比如CSS Selector,JSON,Canvas、SVG和HTTP网络等等,它通过netsniff.js监控网络请求并生成HAR文件,拿到HAR文件后续将很好办了。
  • 缺点:基于PC端出的性能数据
3.Chrome remote debug远程调试
  • 优点:通过Chrome DevTools Protocol来打通PC浏览器和移动设备,功能强大毋庸置疑,我们开发基本上采用这种方式来进行移动端调试和优化
  • 缺点:无法自动化运行,需要翻樯,因为chrome inspect需要访问appspot.com地址

4. Tcpdump&Mimtproxy
  • Tcpdump优点 相信需要测试移动网络抓包场景的同学一定有接触过tcpdump吧?Tcpdump是Linux系统中使用的比较广泛的一款抓包工具,其利用的是libpcap工具,通过监听和记录网卡接口的特定端口的网络数据来实施抓包,生成的文件以pcap结尾。用wireshark直接读取或者通过解析pcap2har工具来生成需要的性能数据,是比较接近真实移动设备环境的一种测试手段。具体的操作方式可以自行搜索,也可以参考我写的上一篇文章的用法介绍。
  • Tcpdump缺点:需要root、高版本手机root成功率极低、容易被还原导致自动化测试任务无法进行,无法解析https请求。

  • MimtProxy优点
    MitmProxy是什么这里就不做赘述,读者可以自己看一下官网介绍(https://mitmproxy.org; ),它本来是被黑客拿来做“中间人攻击”用的利器,我们主要用到的是它对https数据截获的功能。不同于tcpdump的是我们可以对mitmproxy抓取到的数据做高度定制化处理。

  • MimtProxy缺点
    由于其代理的特征,会对传输数据有一定性能上的损耗,不过从版本横向比较的角度来说可以忽略不计;另外mitmproxy获取不到UDP数据包;另外还有一些其他坑就不铺开说了。

关于浏览器的打开方式

监控WebView获取页面的性能数据

灵感来自(https://github.com/jwcqc/WebViewMonitor;) 项目;

  • 操作流程很简单:
  • 通过在被测手机端安装APP,接收服务端下发测试指令集告诉客户端开始进行H5测试
  • 客户端根据指令打开指定浏览器内核的WebView,在访问浏览器之前注入监控js到页面头部
  • 客户端执行完成页面访问后,在本地记录测试数据,回传到服务端
  • 目前支持chrome内核和QQ内核

  • 设计流程图

  • 基于STFService APK的改造
    由于我们整体的测试方案是基于OpenSTF进行定制的,所以我们选择在STFService APK上进行改造,当然我们在开发过程中做了解耦,方便以后升级。
    注入代码如下:

    public void onProgressChanged(int newProgress) {
    if (newProgress == 100) {
    try {
    if (mTimerFinish != null) {
    mTimerFinish.cancel();
    mTimerFinish = null;
    }
    } catch (Exception e) {
    }
    if (!isDone) {
    mTimerFinish = new Timer();
    mTimerFinish.schedule(new TimerTask() {
    @Override
    public void run() {
    //TODO collector.js文件的地址,收集的功能主要在这里面实现
    String injectJs = "https://thisis.inject.js";
    String js = "javascript:" +
    " (function() { " +
    " var script=document.createElement('script'); " +
    " script.setAttribute('type','text/javascript'); " +
    " script.setAttribute('src', '" + injectJs + "'); " +
    " document.head.appendChild(script); " +
    " script.onload = function() {" +
    " startWebViewMonitor();" +
    " }; " +
    " }" +
    " )();";

    onLoadInjectJS(js);
    }
    }, 10000);
    }
    }
    }
  • 性能数据处理代码

    public long[] parsePerformanceData(String data) {
    long[] result = null;
    try {
    JSONObject jsonObject = new JSONObject(data);
    jsonObject = jsonObject.getJSONObject("payload");
    JSONObject timeJsonObj = jsonObject.getJSONObject("navigationTiming");

    final long timeStart = timeJsonObj.optInt("navigationStart");
    final long timeWhite = timeJsonObj.optInt("responseStart") - timeStart;
    final long timeLoad = timeJsonObj.optInt("loadEventEnd") - timeStart;
    final long timeDNS = timeJsonObj.optInt("domainLookupEnd") - timeJsonObj.optInt("domainLookupStart");
    final long timeTCP = timeJsonObj.optInt("connectEnd") - timeJsonObj.optInt("connectStart");
    final long timeDOMParse = timeJsonObj.optInt("domComplete") - timeJsonObj.optInt("domInteractive");
    final long timeDOMReady = timeJsonObj.optInt("domContentLoadedEventEnd") - timeJsonObj.optInt("navigationStart");

    result = new long[]{timeWhite, timeLoad, timeDNS, timeTCP, timeDOMParse, timeDOMReady};
    } catch (Exception e) {
    result = null;
    }
    return result;
    }
  • 针对OpenSTF服务端做了扩展

    • 在STFService/wire.proto中扩展了消息事件
    • 修改了/lib/units/device/plugins/service.js 部分逻辑,增加了部分事件监听和接口
    • 增加了服务端和客户端定制接口和数据处理逻辑event事件
性能数据的Timline瀑布图

通过以上方式拿到性能数据后,需要对pacap文件进行转化,一开始用的是开源工具python版本的pcap2har,开源地址(https://github.com/andrewf/pcap2har; ) 后来为了更好的集成到平台中,我们尝试写了一套node版本的,具体参考公众号之前的文章《nodejs实现pcap转换har》,没错,上面的技术方案也是团队两个小伙子捣鼓出来的,是不是非常棒?

通过开源项目harviewer(地址请参考我上篇文章)将json格式的结果文件快速转换成前端timline格式的报告,当然为了满足我们项目的需要,一样对harview进行了大量的改造,代码细节就不截图了。

测试报告如下:


结束语

前端性能优化

前端性能优化方案,请关注我们部门前端团队奇舞团的公众号,有专业的文章讲解,比如l来自W3C成员的文章《CSS性能优化的8个技巧》等等。

一些疑问
  • 项目历时大半年时间
  • 基于开源项目openstf,对其做了部分重构(前端和数据库)
  • 大家遇到的问题我们也多数碰到了,比如性能问题、掉线问题、offline问题等等
参考文献

https://blog.csdn.net/TMQ1225/article/details/53204476
https://segmentfault.com/a/1190000004010453
http://www.cocoachina.com/ios/20170717/19882.html?utm_source=itdadao&utm_medium=referral
https://github.com/jwcqc/WebViewMonitor
https://github.com/w3c/navigation-timing/issues/21
https://www.w3.org/Submission/first-screen-paint/
https://www.cnblogs.com/littlelittlecat/p/6810294.html

共收到 2 条回复 时间 点赞

作者你好,我想知道你们这个项目是开发人员做的?还是测试人员做的?这个项目的人数是多少?

xiwunongyue 回复

2个测试同学兼职弄的

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up