【App自动化测试】(十四)Android WebView测试方法


1. webview 架构与分析方法

1.1 webview与Hybrid开发

在这里插入图片描述

1.1.1 移动APP三种开发模式

  1. Native APP(原生应用)

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

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

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

  1. Hybrid App (混合应用)

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

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

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

  1. 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:

  1. 正常都会有保留WebView的名称,就算做了二次开发一般也会保留此名称。
  2. 页面组件中名称会存在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 示例

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

注意:

  1. 浏览器是默认打开webview调试开关的。打开任意网站都能够被分析到
  2. 示例中使用的模拟器是MuMu。
  3. 真机中的调试方法和当前一直,确保被调试的手机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版本

  1. 手机上设置中查看

    • 设置–>应用程序管理–>全部–>Android System WebView
      在这里插入图片描述
  1. 电脑Chome浏览器的devTools中查看

    • 连接设备到电脑,打开Android设备的开发者模式,并勾选USB调试授权PC连接到Android设备。
    • 在edge浏览器打开:edge://inspect/#devices
    • 在设备端运行进行了如下设置(开启远程调试)的Webview的demo
      在这里插入图片描述
  1. 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.

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

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)

  
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android应用程序中,WebView是一个常用的控件,用于在应用程序中显示网页。在混淆代码时,需要注意WebView的混淆问题。以下是一些混淆WebView时需要注意的事项: 1.在proguard-rules.pro文件中添加以下代码,以避免WebView的混淆: ```proguard -keepclassmembers class fqcn.of.javascript.interface.for.webview { public *; } -keepclassmembers class * extends android.webkit.WebViewClient { public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap); public boolean *(android.webkit.WebView, java.lang.String); } -keepclassmembers class * extends android.webkit.WebViewClient { public void *(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceError); } -keepclassmembers class * extends android.webkit.WebViewClient { public void *(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceResponse); } -keepclassmembers class * extends android.webkit.WebViewClient { public void *(android.webkit.WebView, android.webkit.WebResourceRequest); } -keepclassmembers class * extends android.webkit.WebViewClient { public android.webkit.WebResourceResponse *(android.webkit.WebView, java.lang.String, java.lang.String); } -keepclassmembers class * extends android.webkit.WebViewClient { public android.webkit.WebResourceResponse *(android.webkit.WebView, android.webkit.WebResourceRequest); } -keepclassmembers class * extends android.webkit.WebViewClient { public void *(android.webkit.WebView, android.webkit.WebBackForwardList); } -keepclassmembers class * extends android.webkit.WebViewClient { public boolean *(android.webkit.WebView, android.view.KeyEvent); } -keepclassmembers class * extends android.webkit.WebViewClient { public void *(android.webkit.WebView, android.net.http.SslError); } -keepclassmembers class * extends android.webkit.WebViewClient { public void *(android.webkit.WebView, android.webkit.ClientCertRequest); } -keepclassmembers class * extends android.webkit.WebViewClient { public void *(android.webkit.WebView, android.webkit.HttpAuthHandler, java.lang.String, java.lang.String); } -keepclassmembers class * extends android.webkit.WebViewClient { public boolean *(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceError); } -keepclassmembers class * extends android.webkit.WebViewClient { public void *(android.webkit.WebView, android.webkit.WebResourceRequest, android.os.Bundle); } -keepclassmembers class * extends android.webkit.WebViewClient { public void *(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceError, android.webkit.WebResourceResponse); } -keepclassmembers class * extends android.webkit.WebViewClient { public void *(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceResponse, android.os.Bundle); } -keepclassmembers class * extends android.webkit.WebViewClient { public void *(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceResponse, android.webkit.WebResourceError); } -keepclassmembers class * extends android.webkit.WebViewClient { public void *(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceResponse, android.webkit.WebResourceError, android.os.Bundle); } ``` 2.在WebViewClient实例中,需要保留onPageFinished()方法中的代码,以确保WebView正确加载图片。具体代码如下: ```java @Override public void onPageFinished(WebView view, String url) { if(!webView.getSettings().getLoadsImagesAutomatically()) { webView.getSettings().setLoadsImagesAutomatically(true); } } ``` 3.在WebView初始化时,需要设置加载图片的选项。具体代码如下: ```java public void init() { if(Build.VERSION.SDK_INT >= 19) { webView.getSettings().setLoadsImagesAutomatically(true); } else { webView.getSettings().setLoadsImagesAutomatically(false); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值