Appium WebView控件定位
背景
移动应用可以粗分为三种:原生应用(native app), 网页应用(web app,或HTML5 app),以及它们的混血儿——混合模式移动应用(hybrid app)。
什么是Hybrid Mobile App
Hybrid app从外观上来看是一个native app,实则只有一个UIWebView,里面访问的是一个web app,如新闻类和视频类的应用普遍采取该策略:native的框架加上web的内容。不同于native app需要针对不同的平台使用不同的开发语言(如使用Objective-C、Swift开发iOS应用,使用Java等开发Android应用,使用C#开发Windows Phone应用),hybrid app允许开发者仅使用一套网页语言代码(HTML5+CSS+JavaScript),即可开发能够在不同平台上部署的类原生应用 。由于hybrid app结合了native app良好用户交互体验和web app跨平台开发的优势,能够显著节省移动应用开发的时间和成本,hybrid app得到越来越多公司的青睐。
参考资料:
native APP,hybrid APP和web APP:https://blog.csdn.net/weixin_41646716/article/details/82190822
怎么判断是Webview控件
WebView的测试
主要作用在混合(Hybrid)的应用,一部分是原生界面和代码,而另一部分是内嵌网页,比如微信、支付宝,内嵌了一个浏览器内核,由浏览器内核实现的,安卓应用中的内嵌的展示网页内容的模块,我们称之为webview。
Appium之WebView自动化:https://www.cnblogs.com/peipei-Study/p/12012422.html
准备测试机
- 模拟器,Android Studio自带的Android 6.0版本模拟器,更高版本不支持
- genymotion的Android 6.0版本模拟器:https://www.genymotion.com
- 木木模拟器、bluestacks默认不支持
- 真机打开webview开关
app修改编译 ---- 需要添加webview调用 ---- 对webview对象加入setWebContentsDebuggingEnable的调用
直接让开发人员在下面这段代码中加入一句代码:
protected void onCrete(Bundle saveInstanceState){
super.onCreate(savedInstanceState);
WebView myWebView = (WebView) findViewById(R.id.xxxwebview);
myWebView.setWebContentsDebuggingEnabled(true); # 加上这句代码
};
如果不知道怎么说,简单总结一句话:“帮忙加一下webview的debug调用”就好。
观察webview内部的工具:chrome inspect
在chrome中输入:chrome://inspect
使用chrome62版本,其他版本有bug,观察到webview内部结构混乱
需要代理上网
chrome://inspect/#devices HTTP/1.1 404 Not Found 空白页问题:
http://centphp.com/view/165
点击 inspect 按钮,新窗口HTTP/1.1 404 Not Found,出现空白页,这是因为Chrome 的devtools 需要联网下载对应的WebView的测试环境,appspot.com在国内不能直接访问。三种解决方法:
方法一:下载devtools 的inspect的 离线开发者调试工具包。
方法二: 修改网络连接,修改hosts文件。
方法三:使用第三方的chromium内核的浏览器,如QQ浏览器。
进入inspect后
使用Chrome://inspect调试 Android 设备上Webview
https://www.jianshu.com/p/66896bec620e
下载对应的chromedriver版本,使用caps配置chromedriver版本
查看webview版本
chrome://inspect/#devices中可以看到
在手机设置中查看
- 使用命令查看
运行脚本时报错:
selenium.common.exceptions.WebDriverException: Message: An unknown server-side error occurred while processing the command. Original error: No Chromedriver found that can automate Chrome ‘64.0.3282’. See https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/web/chromedriver.md for more details.
官方的说明原文
Appium通过管理Chromedriver 实例并在必要时向其代理命令来支持自动化Android网页(在Chrome和内置浏览器中)和受Chrome支持的混合应用程序。它与最新版本的Chromedriver捆绑在一起 ,并通过npm软件包appium-chromedriver (Github:appium-chromedriver)安装。
随着Chromedriver的每次更新,最低支持的Chrome版本都会增加,从而使旧版本的设备通常无法通过捆绑版本自动执行。在Appium服务器日志中,将出现如下错误:
An unknown server-side error occurred while processing the command.
Original error: unknown error: Chrome version must be >= 55.0.2883.0
要解决此问题,必须为Appium提供正确的Chromedriver二进制文件,该二进制文件应与 被测设备上运行的Chrome引擎版本匹配。阅读以下Chromedriver/Chrome compatibility主题,以了解有关查找匹配的Chromedriver可执行文件的更多信息。
有几种方法可以为Appium提供自定义的Chromedriver:
1.安装服务器时
提供--chromedriver_version包含实际版本号的命令行参数
npm install appium --chromedriver_version="2.16"
或在CHROMEDRIVER_VERSION环境变量中指定Chromedriver版本,例如,
CHROMEDRIVER_VERSION=2.20 npm install appium
也可以将其设置为LATEST获取最新版本。
2.启动服务器时
可以在运行时通过指定--chromedriver-executable服务器标志来指定Chromedriver版本 ,以及通过手动下载并放入服务器文件系统的Chromedriver可执行文件的完整路径,例如
appium --chromedriver-executable /path/to/my/chromedriver
3.开始会话时(手动发现)
通过提供chromedriverExecutable上限,可以在会话功能中指定Chromedriver版本,该 上限包含匹配的Chromedriver可执行文件的完整路径,必须手动下载该文件并将其放入服务器文件系统。有关更多详细信息,请参见http://appium.io/docs/zh-CN/writing-running-appium/caps/
4.开始会话时(自动发现)
如果本地文件系统上不存在目标Chrome引擎的版本,则Appium也可以尝试检测目标Chrome引擎的版本并自动为其下载匹配的chromedriver。阅读Automatic discovery of compatible Chromedriver下面的主题以获取更多详细信息。
Chromedriver / Chrome兼容性
您可以在https://raw.githubusercontent.com/appium/appium-chromedriver/master/config/mapping.json中找到Chromedriver版本及其匹配的最低Chrome版本的列表。
从2.46版开始,Google更改了Chromedriver版本控制的规则,因此现在主要的Chromedriver版本对应于可以自动执行的主要Web视图/浏览器版本。按照版本选择文档以手动找到Chromedriver,如果其主要版本等于或高于73,则该浏览器将支持您当前的浏览器/网络视图。
要查找较低版本的Chromedriver(低于73)的最低支持的浏览器,请获取Chromium 源代码,查看发布确认,然后检查kMinimumSupportedChromeVersion 文件中的变量src/chrome/test/chromedriver/chrome/version.cc。(要查找发布承诺,可以使用git log --pretty=format:'%h | %s%d' | grep -i "Release Chromedriver version"。)
可用的Chromedriver版本和发行说明的完整列表在此处。
自动发现兼容的Chromedriver
从Appium 1.8.0开始,Appium可以为测试中的Chrome版本选择正确的Chromedriver。虽然Appium仅在发布Appium版本时与最新发布的Chromedriver捆绑在一起,但可以下载更多Chromedriver版本并将其放置在Appium安装中(不建议这样做,因为升级Appium会删除它们)或放置在自定义位置可以向Appium指示chromedriverExecutableDir所需的功能。此功能是您放置一个或多个Chromedriver可执行文件的目录的绝对路径。
同样,由于可能会发布新版本的Chromedriver,而发布Appium版本时还没有,因此可以通过chromedriverChromeMappingFile 所需功能将自定义的Chromedriver映射到它们支持的最低Chrome版本。这应该是其中包含映射的文件的绝对路径。文件的内容需要可解析为JSON对象,例如:
{
“ 2.42 ”:“ 63.0.3239 ”,
“ 2.41 ”:“ 62.0.3202 ”
}
从Appium 1.15.0开始,可以chromedriverExecutableDir从官方的Google存储中自动下载必要的chromedriver 。该脚本将自动搜索支持给定浏览器/ Web视图的最新chromedriver版本,将其下载(哈希总和也将针对下载的档案进行验证)并添加到chromedriverChromeMappingFile映射中。您需要做的所有事情都是在chromedriver_autodownload启用功能的情况下执行服务器(例如appium --allow-insecure chromedriver_autodownload)。您还可以查看“安全性”文档,以获取有关如何控制潜在的不安全服务器功能的更多详细信息。
解决网络问题
安装Appium后,需要下载Chromedriver,因此可能存在导致安装失败的网络问题。
默认情况下,从中检索Chromedriver https://chromedriver.storage.googleapis.com/。要使用ChromeDriver二进制文件的镜像,请使用npm config属性chromedriver_cdnurl。
npm install appium-chromedriver --chromedriver_cdnurl = http://npm.taobao.org/mirrors/chromedriver
或将属性添加到.npmrc文件中。
chromedriver_cdnurl = http://npm.taobao.org/mirrors/chromedriver
另一种选择是使用PATH变量CHROMEDRIVER_CDNURL。
CHROMEDRIVER_CDNURL = http://npm.taobao.org/mirrors/chromedriver npm install appium-chromedriver
可能还需要调整网络代理和防火墙设置,以允许进行下载。
W3C支持
Chromedriver直到75版才遵循W3C标准。如果遇到类似此问题的Proxy命令错误,请更新您的Chromedriver版本。旧的Android设备不能使用较新的Chrome驱动程序。如果使用“移动JSON有线协议”运行测试,则可以避免该错误。由于主要版本75 W3C模式是Chromedriver的默认模式,尽管它仍可以根据传递的会话功能切换为JSONWP模式。您可以从下载中阅读Chromedriver中的W3C支持历史记录。
chromedriver下载地址:https://chromedriver.storage.googleapis.com/index.html
在Appium测试切换webview步骤
以下是在Appium测试中与webview对话所需的步骤:
- 导航到应用程序中web页面
- 获取上下文列表,它返回一个我们可以访问的上下文列表,例如:NATIVE_APP或WEBVIEW_1
- 切换到你要操作的webview,原来是在NATIVE_APP,切换到WEBVIEW_1,进行相应操作
- 操作完后,返回到原来的app view(通常为NATIVE_APP)
以雪球app进行演示
需要进行的操作,点击“交易”,进入一个webview页面,输入手机和验证码后,点击返回按钮
地址:https://github.com:yuruotong1/11period-Selenium-homework
from appium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
class TestWebview:
def setup(self):
caps = {}
caps["platformName"] = "android"
caps["deviceName"] = "hogwarts"
caps["appPackage"] = "com.xueqiu.android"
caps["appActivity"] = ".view.WelcomeActivityAlias"
caps['autoGrantPermissions'] = True
caps['noReset'] = True
caps['chromedriverExecutable'] = 'D:/develop/chromedriver/2.20.exe'
self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
self.driver.implicitly_wait(15)
def test_webview(self):
# 点击交易
self.driver.find_element(By.XPATH, '//*[@text="交易" and contains(@resource-id, "tab_name")]').click()
# 切换到webview
self.driver.switch_to.context(self.driver.contexts[-1])
# 切换到webview后,使用selenium定位,点击港美股开户
self.driver.find_element(By.CSS_SELECTOR, ".trade_home_xueying_SJY").click()
# 切换窗口
WebDriverWait(self.driver, 30).until(lambda x: len(self.driver.window_handles) > 3)
self.driver.switch_to.window(self.driver.window_handles[-1])
# 显式等待
WebDriverWait(self.driver, 30).until(expected_conditions.element_to_be_clickable((By.CSS_SELECTOR, '[placeholder="请输入手机号"]')))
# 输入手机号/验证码
self.driver.find_element(By.CSS_SELECTOR, '[placeholder="请输入手机号"]').send_keys("12345")
self.driver.find_element(By.CSS_SELECTOR, '[placeholder="请输入验证码"]').send_keys("555555")
# 切换回native窗口
self.driver.switch_to.context(self.driver.contexts[0])
# 点击返回按钮
self.driver.find_element(By.ID, "action_bar_back").click()
思寒老师上课的例子:
def test_webview_context(self):
self.driver.find_element(By.XPATH, "//*[@text='交易' and contains(@resource-id, 'tab')]").click()
# 首次做测试的时候,用于分析当前的上下文
# for i in range(5):
# print(self.driver.contexts)
# sleep(0.5)
# print(self.driver.page_source)
# 坑1:webview上下文出现大概有3s的延迟, android 6.0默认支持,其他的需要打开webview调试开关
# adb shell cat /proc/net/unix | grep webview
WebDriverWait(self.driver, 30).until(lambda x: len(self.driver.contexts) > 1)
# 坑2:chromedriver的版本与chrome版本必须对应
# 坑3:chromedriver可能会存在无法对应chrome版本的情况,需要使用caps的mapping file或者直接chromedriverExecutable
# /Users/seveniruby/projects/chromedriver/all/chromedriver_2.20 --url-base=wd/hub --port=8000 --adb-port=5037 --verbose
self.driver.switch_to.context(self.driver.contexts[-1])
# print(self.driver.page_source)
# print(self.driver.window_handles)
# 使用chrome inspect分析界面控件,需要代理、需要chrome62及以前的版本都可以
# Proxying [POST /wd/hub/session/b2fe71d1-3dff-45df-bc2c-52e9195d5b98/element] to [POST http://127.0.0.1:8000/wd/hub/session/790fc7cf4c186545679b24ce5bbd9699/element] with body: {"using":"css selector","value":".trade_home_info_3aI"}
self.driver.find_element(By.CSS_SELECTOR, ".trade_home_info_3aI").click()
# 首次做测试的时候,用于分析当前的窗口
# for i in range(5):
# print(self.driver.window_handles)
# sleep(0.5)
# 坑4:可能会出现多窗口,所以要注意切换
WebDriverWait(self.driver, 30).until(lambda x: len(self.driver.window_handles) > 3)
self.driver.switch_to.window(self.driver.window_handles[-1])
phone = (By.ID, 'phone-number')
# html定位的常见问题,元素可以找到的时候,不代表可以交互,需要用显式等待
WebDriverWait(self.driver, 60).until(expected_conditions.visibility_of_element_located(phone))
self.driver.find_element(*phone).send_keys("15600534760")
https://blog.csdn.net/lb245557472/article/details/93590156提到的例子:
from appium import webdriver
mobile_desired_caps = {
'platformName': 'Android',
'platformVersion': '7.0',
'deviceName': 'your device name',
"app": r"your.apk",
# 指定Chromedriver存放的地址,或者下边的路径,二者其一即可
"chromedriverExecutableDir": r"C:\path",
# "chromedriverExecutable": r"C:\path\chromedriver.exe",
# 声明中文
"unicodeKeyboard": 'True',
# 声明中文,否则不支持中文
"resetKeyboard": 'True',
# 执行时不重新安装包
'noReset': 'True',
'automationName': 'uiautomator2',
'appPackage': 'your package',
'appActivity': 'your App activity'
}
driver = webdriver.Remote('http://localhost:4723/wd/hub', mobile_desired_caps)
# 点击进入到webview页面
driver.find_elements_by_id("id").click()
# 切换到 webview
webview = driver.contexts[-1]
driver.switch_to.context(webview)
print "###########################################"
print driver.contexts
# [u'NATIVE_APP', u'WEBVIEW_xxxxxxxxx']
# 判断webview上的元素是否存在
print driver.find_element_by_id("btnLogoutConfirmation").is_displayed()
# 返回到原生view
driver.switch_to.context(driver.contexts[0])
Appium WebView控件定位的更多相关文章
- 转载:Robotium之Android控件定位实践和建议(Appium/UIAutomator姊妹篇)
来源于:http://blog.csdn.net/zhubaitian/article/details/39803857 1. 背景 为保持这个系列的一致性,我们继续用SDK自带的NotePad实例应 ...
- Robotium之Android控件定位实践和建议(Appium/UIAutomator姊妹篇)
本人之前以前撰文描写叙述Appium和UIAutomator框架是怎样定位Android界面上的控件的. UIAutomator定位Android控件的方法实践和建议 Appium基于安卓的各种Fin ...
- 【转】Appium基于安卓的各种FindElement的控件定位方法实践
原文地址:http://blog.csdn.net/zhubaitian/article/details/39754041#t11 AppiumDriver的各种findElement方法的尝试,尝试 ...
- Appium基于安卓的各种FindElement的控件定位方法实践和建议
AppiumDriver的各种findElement方法的尝试,尝试的目标应用是SDK自带的Notepad应用. 1. findElementByName 1.1 示例 el = driver.fin ...
- Appium基于安卓的各种FindElement的控件定位
转自:http://www.2cto.com/kf/201410/340345.html 1. findElementByName 1.1 示例 ? 1 2 el = driver.findEleme ...
- appium 控件定位
转自:http://www.2cto.com/kf/201410/340345.html AppiumDriver的各种findElement方法的尝试,尝试的目标应用是SDK自带的Notepad应用 ...
- appium简明教程(10)——控件定位基础
狭义上讲,UI级的自动化测试就是让机器代替人去点来点去的过程. 但机器去点什么(点上面还是点左边),怎么点(是长按还是轻触),这些东西是必须由代码的编写者所指示清楚的. 控件定位就是解决机器点什么的问 ...
- Appium自动化(7) - 控件定位工具之Appium 的 Inspector
如果你还想从头学起Appium,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1693896.html 前言 上一篇文章介绍了另一块控件定 ...
- appium自动化测试(3)-控件定位&中文输入
参考-控件定位 http://www.2cto.com/kf/201410/340345.html appium接口 http://appium.io/slate/en/master/?python# ...
随机推荐
- 原来一条select语句在MySQL是这样执行的《死磕MySQL系列 一》
前言 看到蒋老师的第一篇文章后就收货颇丰,真是句句戳中痛点. 令我记忆最深的就是为什么知道了一个个技术点,却还是用不好 ?不管是蒋老师所说的Redis还是本系列要展开学习的MySQL. 这是一个值得思 ...
- 备战秋招之十大排序——O(n)级排序算法
时间复杂度O(n)级排序算法 九.计数排序 前文说到,19591959 年 77 月,希尔排序通过交换非相邻元素,打破了 O(n^2)的魔咒,使得排序算法的时间复杂度降到了 O(nlog n) 级,此 ...
- 骨架屏css样式
.chiaroscuro { background: #f2f2f2; animation-duration: 1.5s; animation-name: blink; animation-itera ...
- [转]C# 互操作性入门系列(一):C#中互操作性介绍
传送门 C#互操作系列文章: C# 互操作性入门系列(一):C#中互操作性介绍 C# 互操作性入门系列(二):使用平台调用调用Win32 函数 C# 互操作性入门系列(三):平台调用中的数据封送处理 ...
- 【C#】 Stopwatch详解
Stopwatch的命名空间是using System.Diagnostics; 1 namespace System.Diagnostics 2 { 3 // 4 // 摘要: 5 // 提供一组方 ...
- [SWMM]出现问题及解决
1,节点顺序 [错误]:如下图,在SWMM软件中普通节点到出水口的连接线不能正常连接,提示找不到出水口节点,但在inp文件中是存在的! [解决]:需要先写入点节点再写入线节点,即先写入[JUNCTIO ...
- 关于PLSQL中的一些问题总结:在PLSQL中书写DDL等
关于问题前导,使用的数据表中涉及到的字段和类型: 在PLSQL中create.drop.truncate等DDL是没有办法直接执行的. 必须要使用: Execute immediate 'DDL语句' ...
- 整理之BroadcaseReceiver
广播的分类 有序广播:按接收器优先级从高到低接受消息,一次只能有一个接收器处理消息.中途可以被截断. 无序广播:所有接收器同时接受消息并处理,无法拦截. 本地广播:只能在本应用内传播的无需广播.上面两 ...
- vue element-ui 做分页功能之封装
在 vue 项目中的 components 中 创建一个 文件夹,文件夹里创建一个 name(这个名字你随意取).vue <template> <div class=" ...
- Redis的持久化机制与内存管理机制
1.概述 Redis的持久化机制有两种:RDB 和 AOF ,这两种机制有什么区别?正式环境应该采用哪种机制? 我们的服务器内存资源是有限的,如果内存被Redis的缓存占满了怎么办?这就要看Redis ...