webviewv36 支持的 Remote Debugging 特性以及与 appium 的关系

来源:互联网 发布:热敏打印软件 编辑:程序博客网 时间:2024/06/10 22:42

今天晚上花了点时间继续研究了一下appium的源码和相关资料,结合之前群里一直讨论的appium如何支持hybrid页面测试的大话题,输出一个新的折腾结果:webview36支持的Remote Debugging的应用技巧,观点错误之处,还望及时指出,知道的同学也可以继续参与讨论。

注意:本文讨论的是4.4以上的新webview,欢迎有同学另发帖子,讨论selendroid,作为一枚前端开发来说,是希望看到webview逐步进化的,因为这是必然趋势,h5和js在手机上的性能会越来越强劲。

本文主要介绍以下几个内容:
1. Remote Debugging on Android with Chrome
2. Appium的android-hybrid.js以及chromedriver.js部分源码分析
3. chromedriver latest release对hybrid测试的支持参数

demo环境:
Desktop chrome ver: 39.0.2171.95 (64-bit)
chromedriver: 2.13.307650
app:ctrip app
Android: 4.4.2

再次认识一下Hybrid

Hybrid的直译为:杂交,当然了,我们忽略他原来的意思,这个词语本身在app开发中非常常见,它代表一种开发模式,即在app端采用原生(native)技术,一般为oc,java等语言 + web页面开发技术(一般为html5,javascript等相关技术)进行融合,在适当的环节采用适当的实现方式,能够使开发效率达到一个满意值,并且产品的交互等各方面表现能够接近原生,国内像携程,大众点评网的app等均采用了这种模式,那么这个时候问题来了,这些非原生技术实现的页面的载体是什么?相信很多同学都知道,是Webview,ok,现在我们知道,很多时候Hybrid的那些页面都是通过这样一种载体进行展示的,暂且不深入讨论webview,webview正在不断进化,这是我们希望看到的。

注意:区分webkit webview和chrome内核的webview

一些周边帖:《chrome mobile emulation 及周边漫谈和相关应用+想法》
http://testerhome.com/topics/1697

自动化测试在遭遇Hybrid时的窘境

其实,我这里用"窘境"这个词语非常不恰当,因为,当你发现其实这些问题都存在解决方案的时候,你就会发现其实这不算什么,相反,官方支持地很好,而且也正在不断进化。

Remote Debugging on Android with Chrome

官方文档及说明:

The way your web content behaves on mobile can be dramatically different from the desktop experience. Remote debugging with Chrome DevTools lets you debug live content on your Android device from your development machine.

使用Chrome的开发者工具能够让你即时调试在设备上运行的页面,他们是:

Debugging websites in browser tabs.

重点来了:Debugging WebViews in native Android apps. 调试在安卓应用里的webviews
好像是我们想要的......

Screencasting live to your development machine from your Android device.
令人兴奋的casting特性

Accessing your development server on Android using port forwarding and virtual host mapping.
端口映射调试模式,暂时不用管。

继续过滤出来我们关注的信息:

要求

To begin remote debugging, you need:

Chrome 32 or later installed on your development machine.
Chrome 32 + 以上版本(本机)

A USB cable to connect your Android device.
USB线

下面两行又是重点,Appium对webview测试支持的部分策略就跟这个有很大联系
For browser debugging: Android 4.0+ and Chrome for Android.
For app debugging: Android 4.4+ and a WebView configured for debugging.
对于应用调试,4.4以上

Note: Remote debugging requires your version of desktop Chrome to be newer than the version of Chrome for Android on your device. For best results, use Chrome Canary (Mac/Windows) or the Chrome Dev channel release (Linux) on desktop.
推荐安装的桌面版chrome版本

打开我们的chrome开发者工具,我们来玩一把。

环境准备:
真机一个,或者模拟器一个,按照上述官方要求的chrome版本一个。
开启设备的USB调试模式
安装一个被测apk,这里以携程为例:

在地址栏敲入chrome://inspect

我们来到一个干货界面,不出意外,你将看到下面这张图:

我的模拟器界面:

当我们在app上进行操作时,开发者工具自动会将嗅探到的webview给我们列出来。
这个技能可以帮助测试人员自己查看内部元素。

我们只需要按下inspect

我们看到,webview里加载页面的源码信息以及dom结构已经完全展现在我们面前,而且你可以将鼠标停留在某个dom上,正如你所熟悉的开发者工具,它会帮你在app里面高亮显示出对应的元素。

更多调试技巧可以参考这里:https://developer.chrome.com/devtools/docs/remote-debugging

与appium的联系

这里,我只讨论安卓端,在appium的源码中,与这个实现有关系的两个源码文件是https://github.com/appium/appium/blob/master/lib/devices/android/android-hybrid.js
还有一个是
https://github.com/appium/appium/blob/master/lib/devices/android/chromedriver.js

我们先来看android-hybrid.js

androidHybrid.listWebviews = function (cb) {  logger.debug("Getting a list of available webviews");  var webviews = [];  var definedDeviceSocket = this.args.androidDeviceSocket;  this.adb.shell("cat /proc/net/unix", function (err, out) {    if (err) return cb(err);    _.each(out.split("\n"), function (line) {      line = line.trim();      var webviewPid = line.match(/@?webview_devtools_remote_(\d+)/);      if (definedDeviceSocket) {        if (line.indexOf("@" + definedDeviceSocket) ===          line.length - definedDeviceSocket.length - 1) {          if (webviewPid) {            webviews.push(this.WEBVIEW_BASE + webviewPid[1]);          } else {            webviews.push(this.CHROMIUM_WIN);          }        }      } else if (webviewPid) {        // for multiple webviews a list of 'WEBVIEW_<index>' will be returned        // where <index> is zero based (same is in selendroid)        webviews.push(this.WEBVIEW_BASE + webviewPid[1]);      }    }.bind(this));    webviews = _.uniq(webviews);    //...... 后面代码省略

相信很多同学也都看过源码了,client端的get contexts方法对应服务端的这个listWebviews,这个方法非常讨巧,我们清晰地看到,它执行了一条命令,然后利用正则表达式去匹配webview_devtools_remote,见下图:

cat /proc/net/unix

webview_devtools_remote?似曾相识的感觉:
appium的这个策略正是利用了remote debugging特性。

目前为止,我们只是能够嗅探到webview内部的元素,appium接下来会怎么做?
我们应该一下子就能够想到webdriver,所以Appium很自然地对其进行了包装:

androidHybrid.startChromedriverProxy = function (context, cb) {  logger.debug("Connecting to chrome-backed webview");  if (this.chromedriver !== null) {    return cb(new Error("We already have a chromedriver instance running"));  }  var setupNewChromeDriver = function (ocb) {    var chromeArgs = {      port: this.args.chromeDriverPort    , executable: this.args.chromedriverExecutable    , deviceId: this.adb.curDeviceId    , enablePerformanceLogging: this.args.enablePerformanceLogging    };    this.chromedriver = new Chromedriver(chromeArgs, this.onChromedriverExit.bind(this));    this.rememberProxyState();    this.proxyHost = this.chromedriver.proxyHost;    this.proxyPort = this.chromedriver.proxyPort;    this.isProxy = true;    var caps = {      chromeOptions: {        androidPackage: this.args.appPackage, //貌似有戏...        androidUseRunningApp: true //貌似有戏...      }    };

两个核心参数:

chromeOptions: {        androidPackage: this.args.appPackage,         androidUseRunningApp: true      }

chromedriver官网对这两个参数的解释:

Android-specific Desired Capabilities

The following capabilities are applicable to both Chrome and WebView apps:

androidPackage: The package name of the Chrome or WebView app.
androidDeviceSerial: (Optional) The device serial number on which to launch the app (See Multiple Devices section below).
androidUseRunningApp: (Optional) Attach to an already-running app instead of launching the app with a clear data directory.

The following capabilities are only applicable to WebView apps.
androidActivity: Name of the Activity hosting the WebView.
androidProcess: (Optional) Process name of the Activity hosting the WebView (as given by ps). If not given, the process name is assumed to be the same as androidPackage.

尝试直接使用这几个参数

先开启一个wd/hub模式的chromedriver

chromedriver --url-base=wd/hub

简短demo

#!/usr/bin/env python# -*- coding:utf-8 -*-from appium import webdriverif __name__ == "__main__":    capabilities = {        'chromeOptions': {            'androidPackage': 'ctrip.android.view',            'androidUseRunningApp': True,            'androidActivity': 'ctrip.android.view.h5.activity.H5Container'        }    }    hybrid_test_driver = webdriver.Remote("http://127.0.0.1:9515/wd/hub", capabilities)    a = hybrid_test_driver.find_element_by_id("div_czb")    a.click()

注意:这里的androidActivity必须传入,可以用monitor等轻松嗅探到。

运行结果

新webview特性支持表

https://developer.chrome.com/multidevice/webview/overview

说明

本文就webview的测试没有讨论全面,比如appium的向下兼容处理(selendroid)策略,我们的本意是希望webview的进化越来越给力,这样方便的就是广大开发者和testing skill研究人员。

总结

对未来充满无限展望,我们在新webview特性的光环笼罩下,使用chrome dev tools + chromedriver的几个参数就能完成webview内的元素嗅探以及driver操作,我们也希望webview能够继续进化,让那些该死的兼容问题见鬼去吧。



=============================================以下是回帖精华信息============================================================

1.

如果一开始就是用webview的话,其实走的就是 selendroid 的通道了。chrome 这块感觉是给 chrome-based 的webview 用的。Selendroid 使用的是 atom js 注入,来和页面元素交互的。

    // current ChromeDriver doesn't handle more than a single web view    if (this.isChromedriverContext(name)) {      this.startChromedriverProxy(name, next);    } else if (this.isChromedriverContext(this.curContext)) {      this.suspendChromedriverProxy(next);    } else if (this.isProxy) { // e.g. WebView context handled in Selendroid      this.proxyTo('wd/hub/session/' + this.proxySessionId + '/context', 'POST', {name: name}, next);    }



0 0
原创粉丝点击