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

Archer · 2014年12月23日 · 最后由 Jhon 回复于 2017年03月07日 · 2229 次阅读
本帖已被设为精华帖!

今天晚上花了点时间继续研究了一下 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 webdriver


if __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 能够继续进化,让那些该死的兼容问题见鬼去吧。

共收到 20 条回复 时间 点赞

如果一开始就是用 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);
}

👍,chrome 主要还是给 android 上的 chrome 浏览器以及没特化过的 chromium webview。如果特化过还得自己修改点源码自己 build 个 driver。

#2 楼 @luis 是的,其实我文中只讨论了这一种情形~

#1 楼 @lihuazhang 没错,webview 是个大话题,我暂且没有讨论 selendroid

很强大,赞

Archer #18 · 2014年12月23日 Author

#5 楼 @oscar 欢迎大家补铁~

赞!~目前还看不懂,看上去挺强大的,留个脚印先!~

#7 楼 @yangchengtest 看不懂说明不是你的问题,是我的问题。。。。。。

@qddegtya ....呵呵,真不是你的问题,暂时还没开始做 WEBVIEW 这块的内容,完全没有概念。。。

是不是需要 *** 才有内容?我这部 devices 下边显示为空。

好贴!

我也想直接用 webdriver 来测试 webview,绕过 appium server,在 webdriver 库中调用的时候,新建 session 时,报错:unknown error: Failed to run adb command, is the adb server running? 但是我已经手动打开了 adb server,百思不得其解,求指点

麻烦问下,我使用的是小米 3 或者模拟器,都是 4.4.4 的版本,我 pc 的 chrome 版本是 42.0.2311.153m,手机上的 chrome 版本是 41.0.2272.96,为什么我打开 h5 的页面 chrome inspect 是空的呢?(我也用的和你一样的 app)但是如果我打开 chrome 就不是空的。

@gaoxing200851 我也遇到跟你一样的问题,别的 app 的 webview 就是不能再 chrome inspect 里看到@qddegtya 楼主,针对 13 楼的问题,有可能是什么原因导致的?是因为我们的 app 没有在程序里开启 webview 调试?如果是这种情况的话我也用的你 demo 里的 app 为什么在 chrome inspect 还是看不到 webview 的页面元素?

#14 楼 @hardworkingtester @qddegtya 是啊,求指教,是不是因为 *** 翻的不够好?

#6 楼 @qddegtya 你好,我用了你得 demo 试了一下,没能成功
报错信息如下,然后我加了 platformName 跟 deviceName 上去,还是同样的报错~

Traceback (most recent call last):
  File "D:\appium\ccarehybridtest1.py", line 20, in <module>
    driver = webdriver.Remote("http://192.168.10.56:9515/wd/hub", capabilities)
  File "C:\Python34\lib\site-packages\appium\webdriver\webdriver.py", line 35, in __init__
    super(WebDriver, self).__init__(command_executor, desired_capabilities, browser_profile, proxy, keep_alive)
  File "C:\Python34\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 74, in __init__
    self.start_session(desired_capabilities, browser_profile)
  File "C:\Python34\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 123, in start_session
    'desiredCapabilities': desired_capabilities,
  File "C:\Python34\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 175, in execute
    self.error_handler.check_response(response)
  File "C:\Python34\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 166, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: A new session could not be created. (Original error: The following desired capabilities are required, but were not provided: platformName, deviceName)

[Finished in 1.1s with exit code 1]

@gaoxing200851 手机 chrome 上,设置-开发者工具-usb 网页调试打开。。。我也困惑了很久,今天才发现。。

nice!正要找分析 webview 的工具!

#17 楼 @taofei chrome 手机版没有 设置-开发者工具-usb 网页调试打开 这个选项

回复

先确定 adb devices 确定有用,这个有用的话,就可以看到了

恒温 Appium 中 iOS 下的 Hybrid 中提及了此贴 09月26日 11:27
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册