目录
一、Toast信息
Toast是Android中用来显示显示信息的一种机制,和Dialog不一样的是,Toast是没有焦点的,而且Toast显示的时间有限,过一定的时间就会自动消失。
比如:密码输错后,移动端的提示。即是Toast 信息
要求环境:
Appium-Python-Client: 2.1.2
selenium: 4.1.0
Appium:1.6.3开始支持识别Toast内容 :v1.6.3起步
代码实现:
优先配置:desired_caps 配置
'automationName': 'uiautomator2'
例子:微信输入手势密码错误,获取 toast:
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
from appium.webdriver.common.touch_action import TouchAction
desired_caps = {
'platformName': 'Android',
'platformVersion': '8.1',
'deviceName': 'ye_shen_Android',
'appPackage': 'com.tencent.mm',
'appActivity': 'com.tencent.mm.ui.LauncherUI',
'noReset': True,
'autoAcceptAlerts': True,
'newCommandTimeout': 240,
'automationName': 'uiautomator2'
}
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
driver.implicitly_wait(10)
# 点击 微信 我的
loc = (MobileBy.ANDROID_UIAUTOMATOR, 'new UiSelector().resourceId("com.tencent.mm:id/j5t")')
WebDriverWait(driver, 30).until(EC.visibility_of_element_located(loc))
TouchAction(driver).tap(x=540, y=1255).wait(200).perform()
# 点击 服务
locc = (MobileBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("服务")')
WebDriverWait(driver, 30).until(EC.visibility_of_element_located(locc))
ele = driver.find_element(MobileBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("服务")')
TouchAction(driver).tap(ele).wait(200).perform()
# 检查 请输入手势密码
loccs = (MobileBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("请输入手势密码")')
WebDriverWait(driver, 30).until(EC.visibility_of_element_located(loccs))
# 获取起始坐标-九宫格左上顶点
loc = driver.find_element(MobileBy.ID, "com.tencent.mm:id/hrs").location
print(loc)
# 获取九宫格元素大小 height width 宽 和 高
loc_size = driver.find_element(MobileBy.ID, "com.tencent.mm:id/hrs").size
print(loc_size)
# 获取分割后的均值,即下面要用到的九宫格的步长
step = loc_size["width"] / 6
step_x = loc_size["width"] / 6
step_y = loc_size["height"] / 6
# 计算出每个点的坐标
point1 = (loc["x"] + step, loc["y"] + step * 5)
point2 = (point1[0], point1[1] - step * 2)
point3 = (point2[0], point2[1] - step * 2)
point4 = (point3[0] + step * 2, point3[1] + step * 2)
point5 = (point4[0] + step * 2, point4[1] + step * 2)
point6 = (point5[0], point5[1] - step * 2)
point7 = (point6[0], point6[1] - step * 2)
# 进行滑动操作
TouchAction(driver).press(x=point1[0], y=point1[1]).wait(500). \
move_to(x=point2[0], y=point2[1]). \
move_to(x=point3[0], y=point3[1]). \
move_to(x=point4[0], y=point4[1]). \
move_to(x=point5[0], y=point5[1]). \
move_to(x=point6[0], y=point6[1]).release().perform()
# move_to(x=point7[0], y=point7[1]). \ 禁用最后一个手势
# 利用XPATH获取
toast_text = driver.find_element(MobileBy.XPATH, '//*[contains(@text,"密码错误")]').text
print(toast_text)
运行结果:
说明:
第一种方式:toast 获取主要使用一个通用的class属性获取,通过xpath的方式:
//*[@class="android.widget.Toast"]
第二种方式:xpath比如说文本匹配亦可:
loc = (MobileBy.XPATH, '//*[contains(@text,"%s")]' % '密码错误')
try:
WebDriverWait(driver,10,0.02).until(EC.presence_of_element_located(loc))
print(driver.find_element(MobileBy.XPATH,'//*[contains(@text,"密码错误")]').text)
except:
print("没有匹配到toast!!!!!!!")
封装如下:
def is_toast_exist(driver, ele, texts):
# ele = (MobileBy.XPATH, '//*[contains(@text,"%s")]' % '密码错误')
"""
- driver - 传driver
- timeout - 最大超时时间,默认20s 设定为 10
- poll_frequency - 间隔查询时间,默认0.5s查询一次 设定为0.02S
- ele 表达式
- texts 属性
:Usage:
is_toast_exist(driver)
"""
try:
WebDriverWait(driver,10,0.02).until(EC.presence_of_element_located(ele))
print(driver.find_element(MobileBy.XPATH,'//*[contains(@text,%s)]' % texts).text)
except:
print("没有匹配到toast!!!!!!!")
二、H5页面的操作:包括元素定位等
借鉴文章戳这里
一)、定义
1.H5,即是html5,超文本标记语言,用于描述网页内容结构的语言,网页编程中由它有负责描述页面数据和信息。
2.JS,即是JavaScript,广泛用于web应用开发中的脚本语言,负责响应用户的操作,为网页添加动态功能
3.Native App,即传统的原生APP开发模式,Android基于Java语言,底层调用Google的 API;iOS基于Objective-C或者Swift语言,底层调用App官方提供的API
4.Hybrid App,即原生和web的混合开发模式,由原生提供统一的API给js调用,实现跨平台的效果
二)、鉴别方法
1.看断网时页面显示情况
断网时可正常显示页面即原生页面,显示404或报错则为H5页面。
2.看复制文章的提示
比如是文章资讯页面可以长按页面试试,如果出现文字选择、粘贴功能的是H5页面,否则是native原生页面。
注意:有些原生APP开放了复制粘贴功能或者关闭了。而H5的css屏蔽了复制选择功能等等情况。需要通过对目标测试APP进行对比才可知。
3.查看窗口页面的class属性(最经典的鉴别方法)
如果是:Android.webkit.WebView 属性,那代表这个是一个H5,移动端的网页。不是原生控件
三)、优缺点
1.H5
(1)优点:支持跨平台,安卓、ios不需要单独开发,只需要开发一套即可;用户无需下载,打开网址即可访问;开发简单,开发成本低,入门门槛低。
(2)缺点: 每次打开页面,都需重新加载获取数据;过分依赖网络,访问速度无法保证。
2.原生 Native
(1)优点:直接依托于操作系统,交互性最强,性能最好。
(2)缺点 :开发成本高,无法跨平台;不同平台(Android和iOS)需要各自独立开发;开发人员入门门槛较高;app发布审核流程麻烦,维护成本高。
以“学科网”APP为例:
注意: 要从我们的原生控件切换到H5中(手机的html页面中):满足下面两个条件
1).识别到 WebView(Web视图)
2).两个硬性要求:首先.Android版本4.4以上 ;其次.WebView必须为debug版本(开发打开WebView权限的版本)
首先第1点:元素 class 属性是:Android.webkit.WebView:这一步是确认这个页面是html页面。这个属性可以在点击 “进入”一个html页面中的属性中查看。
Android.webkit.WebView 显示不了问题解决办法:
# 在自动化脚本中,进入到对应的H5页面,打印输出当前context,如果一直显示为Natvie App,则webview未开启
print(driver.contexts) # 正常情况能获取得到 ['NATIVE_APP', 'WEBVIEW_com.xkw.client']
# 如果 只是打印出 NATIVE_APP 解决方法:如果只有 ['NATIVE_APP'] 代表当前页面是不能直接使用切换,无法debug
1.模拟器的contexts中有webview,但是有些手机没有
官方答案是:需要将手机root,然后再去获取。
2.找开发人员要解决:apk包需要debug模式(debug模式的来源:'app打包需要开户webview的debug属性setWebContentDebuggingEnabled(true)')
3.打开app对应的h5页面,在 chrome://inspect/#devices 地址中,检查是否显示对应的 webview,如没有,则当前未开启调试模式
开启方式:在WebView类中调用静态方法setWebContentsDebuggingEnabled,这种需要App开发人员操作
if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
满足第一个条件之后,现在再去获取webview页面的元素定位,获取方式如下
1)、chrome:chrome://inspect/#devices,需要FQ 前提是在手机和模拟器打开html网页才能显示。
2)、使用driver.page_source获取html页面
3)、找开发人员要源文件,自己找属性
4)、uc-devtools不需要FQ
A.举例:第一种:1、chrome://inspect,需要FQ
步骤:目的:调试H5页面,查找元素
1、手机与电脑连接,开启USB调试模式,通过adb devices可查看到此设备
2、在手机端(模拟器)打开html的页面,进入H5页面
3、在电脑端Chrome浏览器地址栏输入chrome://inspect/#devices,进入调试模式
步骤 3:如下:
4)、查看显示出来的参数。
注意:下载chrom浏览器驱动:70.0.3538.110 特别注意:只需要下载驱动就行,浏览器版本电脑或者手机没必要下载,你要搞清楚这个是指手机APP里面的html支持的驱动,不是指执行代码的电脑和手机chrom浏览器,半毛钱关系都没有!!!
驱动下载:http://chromedriver.storage.googleapis.com/index.html?path=70.0.3538.110/
在chrom浏览器中输入上面调试模式页面给的H5页面地址:https://m.zxxk.com/activity/hyr/1177?source=mtc
5)、将驱动放到一个本地执行python代码目录下,例如:.\data\chromedriver.exe
还需要注意在启动参数配置desired_caps加上:
'chromedriverExecutable': r".\data\chromedriver.exe",
'chromeOptions': {'w3c': False},
6)、还要注意:contexts上下文操作
获取所有上下文
driver.contexts
获取当前上下文
driver.context
切换上下文:切换 WEBVIEW 网页Html端
方法一:driver.switch_to.context('WEBVIEW_com.xkw.client')
方法二:contexts = driver.contexts
driver.switch_to.context(contexts[-1])
切回原生应用
driver.switch_to.context('NATIVE_APP')
实战场景:
- 启动学科网APP应用,在页面点击banner广告:期中备考 轻享无忧
- 进入html页面
- 点击登录
- 点击返回html页面
- 返回主页。
实战代码:
import time
from appium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from appium.webdriver.common.mobileby import MobileBy
from appium.webdriver.common.touch_action import TouchAction
from appium.webdriver.common.multi_action import MultiAction
desired_caps = {
'platformName': 'Android',
'platformVersion': '8.1',
'deviceName': 'ye_shen_Android',
'appPackage': 'com.xkw.client',
'appActivity': 'com.zxxk.page.main.LauncherActivity',
'noReset': True,
'autoAcceptAlerts': True,
'newCommandTimeout': 240,
'automationName': 'uiautomator2',
'chromeOptions': {'w3c': False},
'chromedriverExecutable': r'D:\exe\70.0.3538.97\chromedriver.exe' # 下载好的驱动放这个路径下
}
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
driver.implicitly_wait(10)
# 进入首页
loc = (MobileBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("首页")')
WebDriverWait(driver, 30, 1).until(EC.presence_of_element_located(loc))
# 点击活动入口
ele = driver.find_element(MobileBy.ANDROID_UIAUTOMATOR, 'new UiSelector().resourceId("com.xkw.client:id/discover_banner")')
TouchAction(driver).tap(ele).wait(200).perform()
time.sleep(5)
# 获取contexts:所有上下文 如果只有 ['NATIVE_APP'] 代表当前页面是不能直接使用切换,无法debug
context = driver.contexts
print(context) # ['NATIVE_APP', 'WEBVIEW_com.xkw.client']
# 切换到H5窗口 :WEBVIEW_com.xkw.client 两种方式
# 第一种:
driver.switch_to.context(context[-1])
# 第二种:
# driver.switch_to.context('WEBVIEW_com.xkw.client')
# 可以打印页面源看是否进入H5页面
print(driver.page_source)
# 这一步骤执行一直报错:不用也罢 TypeError: appium.webdriver.webdriver.WebDriver.find_element() argument after * must be an iterable, not WebElement
# WebDriverWait(driver, 20).until(EC.presence_of_element_located(driver.find_element_by_class_name('login')))
time.sleep(3)
# 以下定位二选一
driver.find_element(MobileBy.CLASS_NAME, 'login').click()
# driver.find_element_by_class_name('login').click()
# 点击手机返回键,返回出手机html页面 回到APP主页
driver.keyevent(4)
time.sleep(3)
driver.keyevent(4)
# 切换到APP主页的原生页面
# 第一种:切换到Native 切换到手机app模式下
# driver.switch_to.context("NATIVE_APP")
# 第二种:切换到手机app模式下
driver.switch_to.context(context[0])
print(driver.page_source)
B.举例:第四种:没网的情况下下载这个工具也是一样可以查看页面元素和驱动:套路都是一样!