目录
- 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 text
- native 无法表达的 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自动化测试方法
方式 | 技术栈 | 优点 | 缺点 |
---|---|---|---|
原生自动化 | uiautomator appium atx | 简单; 不依赖webview调试开关开启。 | 不易维护(对于H5的大部分属性,翻译后存在属性丢失的可能性,非常不利于维护) |
web自动化 | selenium chromedriver minitest | 易维护 | 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
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Cj6vFm5-1669280276329)(assets/1707637-20190621182849626-1094383312-20221024152036-13hdg26.png)]
电脑Chome浏览器的devTools中查看
- 连接设备到电脑,打开Android设备的开发者模式,并勾选USB调试授权PC连接到Android设备。
- 在edge浏览器打开:
edge://inspect/#devices
- 在设备端运行进行了如下设置(开启远程调试)的Webview的demo
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j7nT8F7N-1669280276330)(assets/image-20221024151856-asbyb1b.png)]
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
- 查看手机上显示的内核版本
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iaIUyyCS-1669280276331)(assets/1707637-20190621185113230-870185233-20221024152110-l2jiymd.png)]
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)