目录

1. webview 架构与分析方法1.1 webview与Hybrid开发1.1.1 移动APP三种开发模式1.1.2 native原生开发1.1.2.1 native原生开发说明1.1.2.2 native原生开发组件

1.1.3 Hybrid混合开发webview展示1.1.3.1 Hybrid混合开发中的app webview组件1.1.3.2 webview开发代码展示1.1.3.3 Hybrid混合开发中的app webview组件

1.1.4 基于webview开发的程序1.1.4.1 微信小程序的主流架构1.1.4.2 支付宝小程序1.1.4.3 web应用中的浏览器webview组件

1.1.5 总结:native视角下的webview控件

1.2 webview组件的真正分析方法1.2.1 webview组件分析方法说明1.2.2 如何分析webview控件?1.2.2.1 第一步:打开webview调试开关1.2.2.1.1 代码中的webvie调试开关1.2.2.1.2 各平台webview组件的调试开关开启情况1.2.2.1.3 强行法开webview调试开关的方法

1.2.2.2 第二步:打开带有webview的页面1.2.2.3 第三步:打开浏览器的分析工具1.2.2.4 示例

2. webview自动化测试2.1 webview自动化测试方法2.2 混合自动化技术原理2.3 appium webview 自动化测试前提2.3.1 前提一:chromedriver安装2.3.2 前提二:chromedriver版本选择正确2.3.3 前提三:appium capability参数设置

2.4 appium context 上下文机制2.5 webview自动化演示代码Python版

1. webview 架构与分析方法

1.1 webview与Hybrid开发

1.1.1 移动APP三种开发模式

Native APP(原生应用)

使用Android 提供的默认控件所开发出来的APP,如TextView、imageview、listview等。

优点:android自带SDK组件开发,与android契合度高,速度快,性能好。

缺点:开发成本高,上线时间不固定,影响敏捷交付,新版本更新需要App Store下载。

Hybrid App (混合应用)

即使用Android原生控件,也包含特殊组件WebView。WebView提供了渲染HTML的功能,因此可以展示出一套新的渲染体系。混合型开发先使用原生app做一层包裹,如启动界面,主界面,进入后会通过webview的控件去加载网页。因此后续开发中就可以使用各种H5的开发方式去进行开发。

目前中国大部分APP的开发模式都是混合型的。例如微信、支付宝,原生界面是比较核心的界面,但是有大量细小功能,如果全部放在移动端中会非常庞大,因此这些功能会使用webview来加载一个远程网址,从而实现更多功能的装载,这样也有利于实时更新,如远程变化后,本地渲染就会相应的发生变化。

优点:开发成本中,性能中等,基于原生APP开发衍生而来,开发和更新不需要上线可在后台进行热启动。

Web App

主要方式是使用H5去开发,用户使用手机浏览器去访问。经过定制开发,专门为手机浏览器进行设计的。

优点:开发成本最低,性能差,可随时进行部署使用

1.1.2 native原生开发

1.1.2.1 native原生开发说明

例如图中,通过Android Studio查看开发方式,某些界面开发方式使用Activity,里面会使用类似EditText、TextView这种由Android默认提供的一套组件库,这种就是native原生开发。

1.1.2.2 native原生开发组件

native原生开发组件特征:通过UI Automator Viewer 可以查看到元素的class属性,原生组件的class属性名称中有android.widget。

1.1.3 Hybrid混合开发webview展示

1.1.3.1 Hybrid混合开发中的app webview组件

webview组件特征:通过UI Automator Viewer查看class属性,可以看出class的的名称android.webkit.webview

1.1.3.2 webview开发代码展示

通过Android提供的webview组件来加载html文件,可以是本地网页,也可以是远程网页。

1.1.3.3 Hybrid混合开发中的app webview组件

UIautomationviewer会强制的把H5的控件的一些属性翻译成原生控件的属性。如果是文本翻译成textview;如果是图片则翻译成image之类的;如果是超链接这种特殊控件,且原生组件中没有对应类型的,则统一翻译成android.view.View.

1.1.4 基于webview开发的程序

1.1.4.1 微信小程序的主流架构

1.1.4.2 支付宝小程序

图示中组件名称为com.uc.webview.export.WebView,不是Android默认的android.webkit.webview说明有团队在默认的webview的基础之上做了二次开发。

识别webview的小Tip:

正常都会有保留WebView的名称,就算做了二次开发一般也会保留此名称。页面组件中名称会存在View这种,说明有部分非原生组件未被UI Automator View识别,并翻译成了View。

1.1.4.3 web应用中的浏览器webview组件

上图说明,手机浏览器也是原生app里面包括了webview组件,这个webview的组件去加载了远程的html。

1.1.5 总结:native视角下的webview控件

uiautomator 框架把 webview 组件“翻译”成了原生组件

uiautomatorviewer、appium desktop、weditor 等都可以分析uiautomatorviewer 对java 版本有要求,需要 1**.8.0 60以下的版本**uiautomatorviewer 首次分析 webview 组件时需多刷新一次 控件转换

html 控件尽量用原生控件表达,比如文本转 TextView,图片转 lmage无适合原生控件表达的html控件用android view.View表达 属性转换

html 控件中的属性用原生控件表达,根据不同的版本转换为 resource-id 、content-desc textnative 无法表达的 html 属性会丟失,比如 class href src 等属性

1.2 webview组件的真正分析方法

1.2.1 webview组件分析方法说明

uiautomatorviewer的分析是不完整的 浏览器的分析工具可以分析webview控件完整信息

1.2.2 如何分析webview控件?

1.2.2.1 第一步:打开webview调试开关

1.2.2.1.1 代码中的webvie调试开关

设置成true,打开webview调试开关。

1.2.2.1.2 各平台webview组件的调试开关开启情况

平台webview组件调试开关应用webview组件调试开关可调式as模拟器android6.0默认开启✔️可调式as模拟器android7.0及以上默认关闭关闭❌不可调试as模拟器android7.0及以上默认关闭开启✔️可调式真机默认关闭关闭❌不可调试真机默认关闭开启✔️可调式微信小程序内核 某些老版本默认开启✔️可调式微信小程序内核 默认关闭❌不可调试

1.2.2.1.3 强行法开webview调试开关的方法

root手机修改webview内核xposed使用沙箱,hook webview组件的调用virtualapp自己编译android系统

ps:因为这些方法不常用且有各种局限,所以使用不多,仅限于安全领域研究。使用最多的办法是通过后门开启调试开关。

1.2.2.2 第二步:打开带有webview的页面

webview页面必须要先打开,webview页面只有先打开的情况下,分析工具中的相关内容才会出现。

1.2.2.3 第三步:打开浏览器的分析工具

分析工具类型:

chrome://inspect

最优选择,但是关键组件上使用的是远程软件,需要翻墙,因此对于我们来说不好处理 【推荐】:edge://inspect

使用与chrome相同的内核,界面与调试风格两者十分相似,并且edge还解决chrome中的一些bug。 firefox about:debugging

功能也比较强大,但是因为技术上使用的与chrome不同,存在很大的差异,因此使用过程中不太好适应

1.2.2.4 示例

操作内容:使用手机浏览器,打开百度网站,进行页面元素分析。

注意:

浏览器是默认打开webview调试开关的。打开任意网站都能够被分析到示例中使用的模拟器是MuMu。真机中的调试方法和当前一直,确保被调试的手机app的webview调试开关已经开启。

第一步:手机上打开webview网页

第二步:使用 edge://inspect 进行分析

检测到webview后,点击对应页面信息下的inspect

然后会跳转到调试界面

2. webview自动化测试

2.1 webview自动化测试方法

方式技术栈优点缺点原生自动化uiautomatorappiumatx简单;不依赖webview调试开关开启。不易维护(对于H5的大部分属性,翻译后存在属性丢失的可能性,非常不利于维护)web自动化seleniumchromedriverminitest易维护1. 不适合混合开发(大部分APP都是混合型的,会导致流程割裂,出现原生是一部分,web是另外一部分的情况。如果大部分逻辑是在web里的,那可以使用此方法。);2. 依赖webview调试开关开启。混合自动化appium易维护,通用。1. 技术复杂(因为要维持原生和web两套机制,因此技术比较复杂);2. 依赖webview调试开关开启。

2.2 混合自动化技术原理

混合自动化技术原理示意图:

appium使用多引擎管理自动化,维持一套driver两套session机制。使用chromedriver自动化webview组件。使用native方式自动化native控件。

Appium本身用多个引擎管理自动化,对不同的自动化类型选择不同引擎去进行处理。

Appium自动化测试原理示意图:

2.3 appium webview 自动化测试前提

论坛帖子:chromedriver下载地址与webview自动化关键代码

查看Andriod内置浏览器WebView版本:

手机上设置中查看

设置–>应用程序管理–>全部–>Android System WebView

电脑Chome浏览器的devTools中查看

连接设备到电脑,打开Android设备的开发者模式,并勾选USB调试授权PC连接到Android设备。在edge浏览器打开:edge://inspect/#devices在设备端运行进行了如下设置(开启远程调试)的Webview的demo

adb指令查看(前提,已安装android sdk环境)

连接设备到电脑,打开Android设备的开发者模式,并勾选USB调试授权PC连接到Android设备。打开win+R 输入cmd打开命令窗口,输入指令:adb shell am start -a android.intent.action.VIEW -d https://liulanmi.com/labs/core.html查看手机上显示的内核版本

2.3.1 前提一:chromedriver安装

如果未安装chromedriver会报错,报错内容如下:

No Chromedriver found that can automate Chrome ‘x.x.xxxx’. You could also try to enable automated chromedrivers download server feature. See appium/chromedriver.md at master · appium/appium · GitHub 96 for more details.

国内chromedriver下载地址:淘宝CDN

2.3.2 前提二:chromedriver版本选择正确

chromedriver版本与chrome webview版本存在绑定关系appium有版本判断,版本不对会出出错,错误提示如下:

unknown error:Chrome version must be >=x.x.xxxx

app内置webview组件可能会跟系统默认自带的webview组件不一致,以app使用的webview为准

2.3.3 前提三:appium capability参数设置

因为APP内置的chrome webview的版本不一定与手机浏览器上的版本一致,因此chrome driver的版本在appium中是不能写死的,利用appium的chromedriver自动发现机制,让appium自动的去找适合的chromedriver。

appium chromedriver 自动发现机制中常用的appium capability参数参数:

chromedriverExecutableDir: 指定 chromedriver 可执行文件集合的目录

将可用的chromedriver放在此目录下,当appium发现此配置项后,会去目录中寻找合适的chromedriver。 chromedriverChromeMappingFile: 允许显式指定版本对应关系

强行指定一个版本的chromedriver去测特定的webview版本 showChromedriverLog: 让appium 日志展示 chromedriver 的日志方便排查

appium capability参数设置 ——python:

def setup(self):

desired_caps = {

"platformName":"Android",

"deviceName":"emulator-5554",

"appPackage": "com.example.android.apis",

"appActivity":".ApiDemos",

"chromedriverExecutableDir":"E:\chromedriver_webview_test",

"chromedriverChromeMappingFile":"E:\chromedriver_webview_test\mapping.json",

"showChromedriverLog":True

}

self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

# 使用隐式等待

self.driver.implicitly_wait(10)

appium capability参数设置 ——java:

//简单方案(不推荐):把chromedriver版本写死

caps.setCapability("chromedriverExcutable","E:\chromedriver_webview_test\chromedriver_2.42");

//完善的自动发现机制

caps.setCapability("chromedriverExecutableDir","E:\chromedriver_webview_test");

caps.setCapability("chromedriverChromeMappingFile","E:\chromedriver_webview_test\mapping.json");

caps.setCapability("showChromedriverLog",True);

2.4 appium context 上下文机制

appium context上下文机制: 怎么在原生和webview之间进行切换? 当前原生APP名为native,代表当前属于原生自动化环境下,当通过api切换到webview后,会有一个“WEBVIEW_XXXX”格式的进程名,说明当前处于webview自动化环境下。利用不同的名称来识别是做原生测试还是webview测试。 Api说明:

展示所有的上下文 :contexts ,第一个是原生 NATIVE,剩下的为 webview获得当前的上下文 :current_context切换上下文: switch_to.context('WEBVIEW_XXXX')

2.5 webview自动化演示代码Python版

from appium import webdriver

from appium.webdriver.common.appiumby import AppiumBy

from selenium.webdriver.support.wait import WebDriverWait

"""

模拟器:avd模拟器

测试测试:模拟器再带的ApiDemos

"""

class TestWebView:

def setup_class(self):

desired_caps = {}

desired_caps['platformName'] = 'Android'

desired_caps['platformVersion'] = '6.0'

desired_caps['deviceName'] = 'emulator-5554'

desired_caps['appPackage'] = 'com.example.android.apis'

desired_caps['appActivity'] = '.ApiDemos'

desired_caps['noReset'] = 'true'

#python appium client 2.x 会默认使用w3c,因此此处使用chromeOptions参数吧w3c

desired_caps['chromeOptions']={'w3c':False}

#关键部分:设置chromedriver所在路径

desired_caps['chromedriverExecutableDir']='E:\chromedriver_webview_test'

#设置显式指定chromedriver和webview版本对应关系

#desired_caps['chromedriverChromeMappingFile']='E:\chromedriver_webview_test\mapping.json'

#开启chromedriverlog

desired_caps['showChromedriverLog']=True

self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

# 使用隐式等待

self.driver.implicitly_wait(10)

def teardown(self):

self.driver.quit()

def test_webview(self):

self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,"new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text(\"Views\").instance(0))").click()

self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,"new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text(\"WebView\").instance(0))").click()

#打印上下文情况

#['NATIVE_APP', 'WEBVIEW_com.example.android.apis']

print(self.driver.contexts)

#webview组件加载慢的时候不一定会及时出现webview上下文,最好显示等待

WebDriverWait(self.driver,5).until(lambda driver:len(self.driver.contexts)>1)

#最好显示指定,在多进程同时有webview的时候,最后一个context不一定是当前app的webview

self.driver.switch_to.context('WEBVIEW_com.example.android.apis')

#局部导入By

from selenium.webdriver.common.by import By

self.driver.find_element(By.LINK_TEXT,'Hello World! - 1').click()

#查看当前所在context

#WEBVIEW_com.example.android.apis

print(self.driver.current_context)

查看原文