学习源:
https://www.bilibili.com/video/BV11p4y197HQ
https://blog.csdn.net/weixin_47498728/category_11818905.html
一、移动端测试环境搭建
学习目标
1.能够搭建java 环境
2.能够搭建android 环境
(一)整体思路
我们的目标是Android测试,所以环境需要搭建三个,Java,AndroidSDK,Android模拟器。
为什么要安装这三个环境,我们倒着来说:
Android模拟器:实际上就是一台手机,方便我们给大家展示效果。
AndroidSDK: Android SDK给你提供开发测试所必须的Android API类库
Java:Android的底层是c、C++,应用层用的语言是Java所以需要使用Java环境
(二)环境搭建
java环境
安装JDK1.8
运行jdk-8u151-windows-x64.exe文件默认安装即可(例如我的安装目录:C:\Program Files Java\jdk1.8.0)
配置java环境变量(Windowns7为例)
1.进入我的电脑 ->属性 -> 高级系统设置 -> 环境变量
2.在系统变量下点击新建 ->变量名: JAVALHOME -> 变量值: 比如: c:\Program Files Java jdk1.8.-> 点击确定按钮
3.在系统变量下点击新建 -> 变量名: CLASSPATH -> 变量值:;%JAVA_HOME%1ib;%JAVALHOME%1ib\tools.jar(***变量值最前面有一个”")-> 点击确定按钮
4.在系统变量下找到系统的path变量,进入在最后添加:;%JAVAHOME%bin;%JAVALHOME%jre bin(最前面是一个分号,如果path变量最后已有分号,可不用添加) -> 点击确定按钮
验证环境变量
选择语言
1.win+r 或者开始->搜索框输入cmd
2.在界面运行java -version
Android环境
以windows为例:
将SDK保存到硬盘
Android SDK文件央解压到任意目录(记住这个目录的位置,目录不要有中文)
配置环境变量
选择语言
1.进入我的电脑->属性 -> 高级系统设置 -> 环境变量
2.在系统变量下点击新建 →变量名:ANDROID_HOME → 变量值:D:android-sdk -> 点击确定按钮
3.在系统变量下找到系统的path变量,最后添加: ;%ANDROID-HOME%platform-tools;%ANDROID-HOME%too1s;(最前面是一个分号,如果path变量最后已有分号,可不用添加》 -> 点击确定按钮
验证环境变量
重启命令行工具,命令行输入adb,不报错即可
Andriod模拟器
以模拟器Genymotion为例:
1.执行genymotion-2.11.0-vbox.exe(是一个集合程序,包含genymotion和virtualbox) → 不需要更改配置,直接下一步默认安装
2.安装完enymotion继续等待,会提示安装virtualbox,继续安装,期间会提示安装oracle插件,全部允许安装
3.安装完成后会在桌面展示genymotion和virtualbox两个图标
(三)为虚拟机提供安装apk功能
1.打开virtualbox
2.进入virtualbox -> 管理 -> 导入虚拟电脑
3.点击文件选择(Samsung Galaxy s6 - 5.1.0 API 22 - 1440x2560va) -> 点击下一步
4.勾选 重新初始化所有网卡的MAC地址
5.点击导入按钮 ->等待倒入完成
6.virtualbox列表会展示如下图圈出的选项
二、adb 调试工具
学习目标:
1.能够了解 adb 的工作原理
2.能够应用常用的 adb 命令
(一)adb 的工作原理
1.adb的概念
ADB 全名Android Debug Bridge,是一个调试工具
- 开发安卓应用的程序员必须要掌握
- 测试工程师在做安卓应用测试时,会使用到
- adb的构成和工作原理
提示:只需要知道组成部分和工作原理,会使用即可,面试可能会被问到
adb 包含三个部分:
1.Client端: 运行在开发机器中,即你的开发电脑,用来发送 adb 命令;
2.Daemon 守护进程: 运行在调试设备中,手机或模器,用来接收并执行 adb 命令;
3.Server端: 同样运行在开发机器中,用来管理 Client 端和手机的 Daemon 之间的通信。
小结
adb 工具可以在电脑通过终端命令操作安卓手机/模拟器。
- adb构成
client端,在电脑上,负责发送adb命令
daemon守护进程,在手机上,负责接收和执行adb命令
server端,在电脑上,负责管理client和daemon
- adb构成和工作原理
client端,在电脑上,负责发送adb命令
daemon守护进程,在手机上,负责接收和执行adb命令
server端,在电脑上,负责管理client和daemon之间的通信
adb工作原理
1.client端将命令发送给server端
2.server端会将命令发送给daemon端
3.daemon端进行执行
4.将执行结果,返回给server端
5.server端将结果再返回给client端
(二)adb 常用命令
1.包名和界面名
概念:
包名 (package):决定程序的唯一性 (不是应用的名字)
界面名 (activity) :目前可以理解,一个界面名,对应着一个界面。
2.应用场景
自动化测试需要通过代码的形式告诉手机测试哪个应用程序的哪一个界面,所以需要通过这个命令进行获取。
使用步骤
1.打开需要测试的应用程序
2.输入 adb 命令
查询命令:
Mac/Linux:
adb shell dumpsys window windows | grep mFocused
Windows:
adb shell dumpsys window windows | findstr mFocusedApp
示例
作用:获取设置程序的包名和界面名
1.先在模拟器或手机中打开《设置》应用程序
2.输入对应平台的命令
结果如下:
mFocusedApp=AppwindowToken{53309da token=Token{2e2fa785ActivityRecord{2928d4fc u0 com.android.settings/.Settings t1127}}}
其中:
- 包名为: com.android.settings
- 界面名为:.settings
注意点
界面名可能会在和同事沟通交流或网站的文章中翻译为启动名
2.文件传输[应用]
(1)发送文件到手机
(2)从手机中拉取文件
(1)发送文件到手机
将手机需要的数据(数据库文件) 在电脑上调整好,直接发送给手机
命令格式
adb push 电脑的文件路径 手机的文件夹路径
示例
作用:将桌面的 a.txt 发送到手机的 sd 卡
adb push c:\users hm\Desktop\a.txt /sdcard
(2)从手机中拉取文件
将手机产生的文件(数据库文件,日志文件) 拉取到电脑中
应用场景
将手机产生的文件 (数据库文件,日志文件) 拉取到电脑中命令格式
adb pull 手机的文件路径 电脑的文件夹路径
示例
作用:将手机的 sd 卡的 a.txt 拉取到桌面
adb pull /sdcard/a.txt c: users\hm\Desktop
3. 获取app启动时间[应用]
应用场景
如果企业对应用程序的启动速度有要求,则需要使用这个命令进行测试2.测试标准:参照同类软件,启动时间不能超出一倍即可
命令格式
adb she11 am start -w 包名/启动名
示例
作用:启动 com.android.settings 程序并且进入主界面(.Settings )
adb shell am start -w com.android.settings/.Settings
解释:
1.ThisTime :该界面( activity )启动耗时 (毫秒)
2.TotalTime:应用自身启动耗时= ThisTime +应用application 等资源启动时间 (毫秒)
3.waitTime:系统启动应用耗时 = TotalTime +系统资源启动时间 (毫秒)
4.获取手机日志[应用]
应用场景
将bug的日志信息发送给开发人员,便于开发人员定位bug
使用步骤
1.打开需要测试的应用程序
2.找到触发bug的位置
3.使用查看日志命令adb 1ogcat
4.触发bug
5.获取日志信息
命令格式
adb logcat
注意:实时监听操作
其它命令
其它命令
1、安装 app 到⼿机:adb install 路 径/xx.apk
2、卸载⼿机上的 app:adb uninstall 包名
3、 获取当前电脑已经连接设备和对应的设备号 :adb devices
4、 进⼊到安卓⼿机内部的linux 系统命令⾏中 :adb shell
5 、启动 adb 服务端:adb start-server
6 、停⽌ adb 服务端:adb kill-server
7、查看 adb 帮助: adb --help
三、Appium自动化测试框架
1.能够安装Appium 桌面客户端
2.能够安装Appium-python 库
Appium介绍
Appium是一个移动端的自动化框架,可用于测试原生应用,移动网页应用和混合型应用,且是跨平台的,可用于iOs和Android操作系统。原生的应用是指用andrid或iOS的sdk编写的应用,移动网页应用是指网页应用,类似于ios中safari应用或者Chrome应用或者类浏览器的应用。混合应用是指一种包裹webview的应用,原生应用于网页内容交互性的应用。
重要的是Appium是跨平台的,何为跨平台,意思就是可以针对不同的平台用一套api来编写测试脚本,
Appium自动化测试环境搭建
我们使用Appium和python来进行自动化测试,需要安装两个东西,一个是Appium的客户端,一个是Appium.python库。这两个需要安装的东西在加上手机就可以进行自动化测试。
它们之间的关系是: python代码·>Appium-python库->Appium -> 手机。
Appium桌面客户端安装方式
1.运行appium-desktop-Setup-1.6.2.exe,默认安装即可
2.启动客户端,按图片步骤 1-> 2->3-> 4 设置
3.启动成功展示如下图
Appium-python库安装
命令行安装(需要联网)
pip3 insta17 Appium-Python-Client
可能出现的问题:
错误提示: could not create /usr/local/lib/python2.7/dist-packages/virtualenv_support’: Permission denied
权限问题,使用管理员运行cmd
hello appium 初体验
-
能够使用 appium 启动任意应用程序
-
能够了解 “前置代码” 中各项参数的含义
01_hello-appium_快速体验_哔哩哔哩_bilibili
应用场景
在做app自动化的时候,我们肯定是针对某个产品、某个软件进行测试,那么我们一定是先让模拟器或
真机帮我们打开这款软件才可以。所以接下来要学的就是如何打开某个应用程序。
需求
使用以下步骤可以打开模拟器中的 《设置》 应用程序
步骤
-
打开手机模拟器
-
打开appium工具
-
创建一个python项目,取名为 hello_appium
-
创建一个 demo.py 文件
-
将下面代码直接复制,并运行即可
# 导模块
from appium import webdriver
# 创建一个字典,包装相应的启动参数
desired_caps = dict()
# 需要连接的手机的平台(不限制大小写)
desired_caps['platformName'] = 'Android'
# 需要连接的手机的版本号(比如 5.2.1 的版本可以填写 5.2.1 或 5.2 或 5 ,以此类推)
desired_caps['platformVersion'] = '5.1'
# 需要连接的手机的设备号(andoird平台下,可以随便写,但是不能不写)
desired_caps['deviceName'] = '192.168.56.101:5555'
# 需要启动的程序的包名
desired_caps['appPackage'] = 'com.android.settings'
# 需要启动的程序的界面名
desired_caps['appActivity'] = '.Settings'
# 连接appium服务器
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
# 退出
driver.quit()
启动过程
appium的启动实际上是在本机使用了4723端口开启了一个服务
我们写的 python 代码会访问本机的 appium 服务器,并获取 driver 对象
appium 会将我们的 driver 对象调用的方法转化成 post 请求,提交给appium服务器
appium 通过接收到的 post 请求发送给手机,再由手机进行执行
如何使用appium打开任意一个应用程序
步骤
1.打开要测试的应用
2.使用 adb 命令获取包名和界面名
3.修改 desired_caps 字典中的appPackage和appActivity 的参数
如果测试的设备版本号发生变化?
修改 desired_caps 字典中的 platformVersion 的参数
如果测试的设备平台发生变化?
修改desired_caps字典中的 platformName的参数
Appium 基础操作 API
-
能够使用 appium 在脚本内启动其他 app
-
能够使用 appium 获取包名和界面名
-
能够使用 appium 关闭 app 和 驱动对象
-
能够使用 appium 安装和卸载 app
-
能够使用 appium 将应用置于后台
一. Appium 基础操作 API
前置代码
from appium import webdriver
desired_caps = dict()
# 手机参数
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# 应用参数
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
# 获取driver
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
# 退出driver
driver.quit()
1.在脚本内启动其他 app
应用场景
如果一个应用需要跳转到另外一个应用,就可以使用这个 api 进行应用的跳转,就像我们通过外卖应用下订单之后会跳转到支付应用一样。
方法名和参数
# 脚本内启动其他app
# 参数:
# appPackage:要打开的程序的包名
# appActivity:要打开的程序的界面名
driver.start_activity(appPackage, appActivity)
示例
打开《设置》应用程序,等待三秒后跳转到《短信》应用程序
from appium import webdriver
desired_caps = dict()
# 手机参数
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# 应用参数
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
# 获取driver
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
# 跳转到短信
driver.start_activity('com.android.mms', '.ui.ConversationList')
time.sleep(5)
# 退出driver
driver.quit()
2.获取 app 的包名和界面名
应用场景
当我们从一个应用跳转到另外一个应用的时候,想输出其包名、界面名或者想在报告中展现对应信息,我们就可以调用这个属性来进行获取。
属性名
# 获取包名
driver.current_package
# 获取界面名
driver.current_activity
示例
打开《设置》应用程序后输出当前的包名和界面名
from appium import webdriver
desired_caps = dict()
# 手机参数
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# 应用参数
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
# 获取driver
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
# 打印当前包名
print(driver.current_package)
# 打印当前界面名
print(driver.current_activity)
# 退出driver
driver.quit()
结果
com.android.settings
.Settings
3.关闭 app 和 驱动对象
应用场景
有的时候我们需要关闭某个应用程序后,再打开新的应用。那么如何关闭应用程序呢?
方法名
# 关闭当前操作的app,不会关闭驱动对象
driver.close_app()
# 关闭驱动对象,同时关闭所有关联的app
driver.quit()
示例
打开《设置》,使用 close_app() 方法关闭,再尝试使用 quit() 方法,最后打印当前程序的包名,观察区别
from appium import webdriver
desired_caps = dict()
# 手机参数
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# 应用参数
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
# 获取driver
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
# 关闭应用
driver.close_app()
# 退出driver
driver.quit()
结果
使用 quit() 后会报错,使用close_app() 不会报错
小结
close_app() 不会关闭驱动对象,只会关闭应用
quit() 会关闭驱动对象
4.安装和卸载以及是否安装 app
应用场景
一些应用市场的软件可能会有一个按钮,如果某一个程序已经安装则卸载,如果没有安装则安装
方法名
# 安装app
# 参数:
# app_path:apk路径
driver.install_app(app_path)
# 卸载app
# 参数:
# app_id:应用程序包名
driver.remove_app(app_id)
# 判断app是否已经安装
# 参数:
# app_id:应用程序包名
# 返回值:
# 布尔类型,True为安装,False为没有安装
driver.is_app_installed(app_id)
示例
如果《安智市场》已经安装,则卸载《安智市场》,如果没有则安装
from appium import webdriver
desired_caps = dict()
# 手机参数
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# 应用参数
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
# 获取driver
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
if driver.is_app_installed("cn.goapk.market"):
driver.remove_app("cn.goapk.market")
else:
driver.install_app("/Users/Yoson/Desktop/anzhishichang.apk")
# 退出driver
driver.quit()
5.将应用置于后台
应用场景
银行类 app 会在进入后台一定时间后,如果再回到前台页面会重新输入密码,如果需要自动化测试这种功能:
# app放置到后台一定时间后再回到前台,模拟热启动
# 参数:
# seconds:后台停留多少秒
driver.background_app(seconds)
示例
打开《设置》应用,进入后台 5 秒,再回到前台
from appium import webdriver
desired_caps = dict()
# 手机参数
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# 应用参数
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
# 获取driver
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
time.sleep(3)
print("---- 准备进入后台 ----")
# 进入后台5秒,再回到前台
driver.background_app(5)
print("---- 以及回到前台 ----")
time.sleep(3)
# 退出driver
driver.quit()
注意点
热启动:表示进入后台回到前台
冷启动:关机再开这种切断电源的行为
四、UIAutomatorViewer
UIAutomatorViewer初体验
学习目标:
能够使用 UIAutomatorViewer 获取元素的特征信息
应用场景
定位元素的时候必须根据元素的相关特征来进行定位,而 UIAutomatorViewer 就是用来获取元素特征的。
简介
UIAutomatorViewer 用来扫描和分析 Android 应用程序的 UI 控件的工具。
使用步骤
1.进入SDK目录下的目录
mac 在 tools/bin 目录下,打开 uiautomatorviewer
windows 在 tools 目录下,打开 uiautomatorviewer.bat
2.电脑连接真机或打开android模拟器
3.启动待测试app
4.点击 uiautomatorviewer 的左上角 Device Screenshot (从左数第二个按钮)
5.点击希望查看的控件
6.查看右下角 Node Detail 相关信息
示例
查看《设置》应用程序右上角 ”放大镜“ 按钮的 ”resource-id“
1.打开 uiautomatorviewer
2.打开 android 模拟器
3.启动《设置》应用程序
4.点击 Device Screenshot 按钮
5.点击 ”放大镜“ 按钮
6.查看 Node Detail 中的 ”resource-id“ 信息
注意点
1.自动打开的命令行窗口不要关
如果关了,整个工具也会关闭
2.打开uiautomatorviewer闪退
解决方案:
jdk版本问题造成的,jdk为1.9时可能会出现这个问题,请换成1.8的版本
3.点击第二个按钮报错
解决方案:重启adb
-
adb kill-server
-
adb start-server
元素定位操作 API
学习目标
-
能够分别使用 id、class、xpath 定位某一个元素
-
能够分别使用 id、class、xpath 定位某一组元素
应用场景
计算机不像人一样 ”聪明“,我们需要通过元素定位来获取元素,才能让计算机帮我们 ”操作“ 这个元素。
步骤
打开 uiautomatorviewer 工具
打开模拟器或真机
通过 uiautomatorviewer 工具获取想要进行操作的元素的 Node Detail 信息
通过元素定位 API 进行定位
对元素进行相关操作
注意点
元素的定位基于当前屏幕范围内展示的可见元素。
1.定位一个元素【掌握】
应用场景
想要对按钮进行点击,想要对输入框进行输入,想要获取文本框的内容,定位元素是自动化操作必须要使用的方法。只有获取元素之后,才能对这个元素进行操作。
方法名
# 通过id定位一个元素
# 参数:
# id_value:元素的resource-id属性值
# 返回值:
# 定位到的单个元素
driver.find_element_by_id(id_value)
# 通过class_name定位一个元素
# 参数:
# class_value:元素的class属性值
# 返回值:
# 定位到的单个元素
driver.find_element_by_class_name(class_value)
# 通过xpath定位一个元素
# 参数:
# xpath_value:定位元素的xpath表达式
# 返回值:
# 定位到的单个元素
driver.find_element_by_xpath(xpath_value)
示例
通过 id 的形式,定位 ”放大镜“ 按钮,并点击
通过 class 的形式,定位 ”输入框“,输入 ”hello“
通过 xpath 的形式,定位"收起"按钮,并点击
关键代码
# 通过 id 的形式,定位 ”放大镜“ 按钮,并点击
driver.find_element_by_id("com.android.settings:id/search").click()
# 通过 class 的形式,定位 ”输入框“,输入 ”hello“
driver.find_element_by_class_name("android.widget.EditText").send_keys("hello")
# 通过 xpath 的形式,定位"收起"按钮,并点击
driver.find_element_by_xpath("//*[@content-desc='收起']").click()
小结:
-
find_element_by_id
传入的参数: resource-id 的值 -
find_element_by_class_name
传入的参数: class 的值 -
find_element_by_xpath
传入的参数:xpath 表达式。
注意点:
如果很多元素特征相同,使用 find_element_by_xxx 的方法会找到第一个
也就是说,尽量去找元素特征有唯一性的“特征”来定位
2.定位一组元素【掌握】
应用场景
和定位一个元素相同,但如果想要批量的获取某个相同特征的元素,使用定位一组元素的方式更加方便。
方法名
# 通过id定位一组元素
# 参数:
# id_value:元素的resource-id属性值
# 返回值:
# 列表,定位到的所有符合调价你的元素
driver.find_elements_by_id(id_value)
# 通过class_name定位一组元素
# 参数:
# class_value:元素的class属性值
# 返回值:
# 列表,定位到的所有符合调价你的元素
driver.find_elements_by_class_name(class_value)
# 通过xpath定位一组元素
# 参数:
# xpath_value:定位元素的xpath表达式
# 返回值:
# 列表,定位到的所有符合调价你的元素
driver.find_elements_by_xpath(xpath_value)
示例
通过 id 的形式,获取所有 resource-id 为 ”com.android.settings:id/title“ 的元素,并打印其文字内容
通过 class_name 的形式,获取所有class 为 ”android.widget.TextView“ 的元素,并打印其文字内容
通过 xpath 的形式,获取所有包含 ”设“ 的元素,并打印其文字内容
关键代码
# 通过 id 的形式,获取所有 resource-id 为 ”com.android.settings:id/title“ 的元素,并打印其文字内容
titles = driver.find_elements_by_id("com.android.settings:id/title")
for title in titles:
print(title.text)
# 通过 class_name 的形式,获取所有class 为 ”android.widget.TextView“ 的元素,并打印其文字内容
text_views = driver.find_element_by_class_name("android.widget.TextView")
for text_view in text_views:
print(text_view.text)
# 通过 xpath 的形式,获取所有包含 ”设“ 的元素,并打印其文字内容
elements = driver.find_element_by_xpath("//*[contains(@text,'设')]")
for element in elements:
print(element.text)
小结:
-
find_elements_by_id
传入的参数: resource-id 的值 -
find_elements_by_class_name
传入的参数: class 的值 -
find_elements_by_xpath
传入的参数:xpath 表达式。
概念:
如果通过一组的方式进行定位,获取的返回值不再是一个元素。而是一个列表,列表中装着所有符合这个特征的元素。
3.定位元素的注意点【了解】
应用场景
了解这些注意点可以以后在出错误的时候,更快速的定位问题原因。
示例
使用 find_element_by_xx 或 find_elements_by_xx 的方法,分别传入一个没有的 ”特征“ 会是什么结果呢?
核心代码
driver.find_element_by_id(“xxx”)
driver.find_elements_by_id(“xxx”)
小结
1.如果使用 find_element_by_xx 方法,如果传入一个没有的特征,会报NoSuchElementException
的错误。
2.如果使用 find_elements_by_xx 方法,如果传入一个没有的特征,不会报错,会返回一个空列表
元素等待
学习目标
能够使用隐式等待来定位元素
能够使用显式等待来定位元素
应用场景
可能由于一些原因,我们想找的元素并没有立刻出来,此时如果直接定位可能会报错,比如以下原因:
1.由于网络速度原因
2.服务器处理请求原因
3.电脑配置原因
概念
WebDriver定位页面元素时如果未找到,会在指定时间内一直等待的过程
元素等待一共分为两种类型
1.显式等待
2.隐式等待
1.隐式等待
示例
在 5 秒钟内,在《设置》程序中的 ”返回“ 按钮,如果找到则点击。如果找不到则观察对应错误信息。
核心代码
from selenium.webdriver.support.wait import WebDriverWait
# -----
# 启动应用参数
# -----
driver.implicitly_wait(5)
search_button = driver.find_element_by_xpath("//*[contains(@content-desc,'收起')]")
search_button.click()
方法参数解释
# 参数:
# timeout:超时的时长,单位:秒
implicitly_wait(timeout)
关键方法:
通过 driver 对象调用implicitly_wait 方法
设置超时时间
作用:
在设置了超时时间之后,后续所有的定位元素的方法都会在这个时间内等待元素的出现。
如果出现了,直接进行后续操作。
如果没有出现,报错,NoSuchElementException。
2.显示等待
应用场景
针对所有定位元素的超时时间设置为不同的值的时候
概念
等待元素加载指定的时长,超出时长抛出TimeoutException异常
步骤
1.导包
2.创建 WebDriverWait 对象
3.调用 WebDriverWait 对象的 until 方法
示例
在 5 秒钟内,每 1 秒在《设置》程序中的 ”返回“ 按钮,如果找到则点击。如果找不到则观察对应错误信息。
核心代码
from selenium.webdriver.support.wait import WebDriverWait
# -----
# 启动应用参数
# -----
# 创建WebDriverWait对象
wait = WebDriverWait(driver, 5, poll_frequency=1)
# 获取元素并设置超时时间和频率
search_button = wait.until(lambda x: x.find_element_by_xpath("//*[contains(@content-desc,'收起')]"))
# 点击搜索按钮,没有显示等待
search_button.click()
方法参数解释
# 参数:
# driver:驱动对象
# timeout:超时的时长,单位:秒
# poll_frequency:检测间隔时间,默认为0.5秒
# 返回值:
# WebDriverWait对象
WebDriverWait(driver, timeout, poll_frequency=0.5)
# 参数:
# method:lambda查找元素表达式
# 返回值:
# 定位到的元素,如果没有定位到会抛出TimeoutException异常
wait.until(method)
隐式等待和显式等待的选择
作用域:
显式等待为单个元素有效(更灵活),隐式为全局元素(更简单)
方法:
显式等待方法封装在 WebDriverWait 类中,而隐式等待则直接通过 driver 实例化对象调用
关于 sleep 的形式?
sleep 是固定死一个时间,不是不行,是不推荐。
元素等待可以让元素出来的第一时间进行操作。sleep 可能造成不必要的浪费。
元素操作API
学习目标
1.能够使用代码点击按钮
2.能够使用代码对输入框输入文字3.能够使用代码对输入框清空文字
4.能够使用代码获取元素的文本内容
5.能够使用代码获取元素的位置和大小
6.能够使用代码根据属性名获取元素的属性值
1.点击元素click()
应用场景
需要点击某个按钮的时候使用
方法名
# 对element按钮进行点击操作
element.click()
示例
-
打开《设置》
-
点击放大镜按钮
driver.find_element_by_id("com.android.settings:id/search").click()
2.输入和清空输入框内容 send_keys() 和 clear()
应用场景
需要对输入框进行输入或清空的时候使用
方法名
# 对element输入框进行输入操作
# 参数:
# value:输入的内容
element.send_keys(value)
# 对element输入框进行输入操作
element.clear()
示例
1.打开《设置》
2.点击 ”放大镜“
3.输入 ”hello“
4.暂停 2 秒
5.清空所有文本内容
6.暂停 5 秒
7.输入 ”你好“
默认输入中文无效,但不会报错,需要在 ”前置代码“ 中增加两个参数
desired_caps[‘unicodeKeyboard’] =True
desired_caps[‘resetKeyboard’] = True
核心代码
desired_caps[‘unicodeKeyboard’] =True
desired_caps[‘resetKeyboard’] = True
...
driver.find_element_by_id("com.android.settings:id/search").click()
edit_text = driver.find_element_by_class_name("android.widget.EditText")
edit_text.send_keys("hello")
time.sleep(2)
edit_text.clear()
time.sleep(5)
edit_text.send_keys("你好")
3.获取元素的文本内容
应用场景
需要获取按钮、文本框、输入框等控件的文本内容时使用
属性名
# 获取element控件的文本内容
# 返回值:
# 控件的文本内容
element.text
示例
1.打开《设置》
2.获取所有 resource-id 为 ”com.android.settings:id/title“ 的元素,并打印其文字内容
核心代码
titles = driver.find_elements_by_id("com.android.settings:id/title")
for title in titles:
print(title.text)
4.获取元素的位置和大小
应用场景
需要获取元素的位置和大小的时候使用
属性名
属性名
# 获取element的位置
# 返回值:
# 字典,x为元素的x坐标,y为元素的y坐标
element.location
# 获取element的大小
# 返回值:
# 字典,width为宽度,height为告诉
element.size
# 获取element的位置
# 返回值:
# 字典,x为元素的x坐标,y为元素的y坐标
element.location
# 获取element的大小
# 返回值:
# 字典,width为宽度,height为告诉
element.size
示例
1.打开《设置》
2.获取 ”放大镜“ 的位置和大小
核心代码
search_button = driver.find_element_by_id("com.android.settings:id/search")
print(search_button.location)
print(search_button.size)
5.获取元素的属性值
应用场景
根据特征定位到元素后,使元素的属性名获取对应的属性值
方法名
# 对element进行点击操作
# 参数:
# value:要获取的属性名
# 返回值:
# 根据属性名得到的属性值
element.get_attribute(value) # value:元素的属性
示例
1.打开《设置》
2.获取所有 resource-id 为 ”com.android.settings:id/title“ 的元素
3.使用 get_attribute 获取这些元素的 enabled、text、content-desc、resource-id、class 的属性值
核心代码
titles = driver.find_elements_by_id("com.android.settings:id/title")
for title in titles:
print(title.get_attribute("enabled"))
print(title.get_attribute("text"))
print(title.get_attribute("name"))
# 特殊:resourceId、ClassName固定属性名
print(title.get_attribute("resourceId"))
print(title.get_attribute("ClassName"))
注意点:
value=‘text’ 返回text的属性值
value=‘name’ 返回content-desc / text属性值
value=‘className’ 返回 class属性值,只有 API=>18(即安卓版本大于4.3) 才能支持
value=‘resourceId’ 返回 resource-id属性值,只有 API=>18 (即安卓版本大于4.3) 才能支持
滑动和拖拽事件
学习目标
1.能够使用 swipe 滑动屏幕
2.能够使用 scroll 滑动屏幕
3.能够使用 drag_ahd_drop 滑动屏幕
应用场景
我们在做自动化测试的时候,有些按钮是需要滑动几次屏幕后才会出现,此时,我们需要使用代码来模拟手指的滑动,也就是我们将要学习的滑动和拖拽事件
swipe 滑动事件
driver.swipe(起始x坐标起始y坐标,结束x坐标结束y坐标持续时间ms)
概念
从一个坐标位置滑动到另一个坐标位置,只能是两个点之间的滑动。
方法名
# 从一个坐标位置滑动到另一个坐标位置,只能是两个点之间的滑动
# 参数:
# start_x: 起点X轴坐标
# start_y: 起点Y轴坐标
# end_x: 终点X轴坐标
# end_y: 终点Y轴坐标
# duration: 滑动这个操作一共持续的时间长度,单位:ms
driver.swipe(start_x, start_y, end_x, end_y, duration=None)
示例1
模拟手指从(100, 2000),滑动到(100, 1000)的位置
核心代码
driver.swipe(100, 2000, 100, 1000)
示例2
模拟手指从(100, 2000),滑动到(100, 100)的位置
核心代码
driver.swipe(100, 2000, 100, 100)
示例3
模拟手指从(100, 2000),滑动到(100, 100)的位置,持续5秒
核心代码
driver.swipe(100, 2000, 100, 100, 5000)
小结
距离相同时,持续时间越长,惯性越小
持续时间相同时,手指滑动的距离越大,实际滑动的距离也就越大
为什么每次运行滑动的时候会有一些误差
cpu工作量大会导致计时器的时间和真实时间有误差
scroll 滑动事件
driver.scroll(起始元素, 结束元素)
概念
从一个元素滑动到另一个元素,直到页面自动停止。
方法名
# 从一个元素滑动到另一个元素,直到页面自动停止
# 参数:
# origin_el: 滑动开始的元素
# destination_el: 滑动结束的元素
driver.scroll(origin_el, destination_el)
示例
从 “存储” 滑动到 “更多”
核心代码
save_button = driver.find_element_by_xpath("//*[@text='存储']")
more_button = driver.find_element_by_xpath("//*[@text='更多']")
driver.scroll(save_button, more_button)
小结
不能设置持续时间,惯性很大
drag_and_drop 拖拽事件
driver.drag_and_drop(起始元素, 结束元素)
概念
从一个元素滑动到另一个元素,第二个元素替代第一个元素原本屏幕上的位置。
方法名
# 从一个元素滑动到另一个元素,第二个元素替代第一个元素原本屏幕上的位置
# 参数:
# origin_el: 滑动开始的元素
# destination_el: 滑动结束的元素
driver.drag_and_drop(origin_el, destination_el)
示例
将 “存储” 拖拽到 “更多”
核心代码
save_button = driver.find_element_by_xpath("//*[@text='存储']")
more_button = driver.find_element_by_xpath("//*[@text='更多']")
driver.drag_and_drop(save_button, more_button)
小结
不能设置持续时间,没有惯性
滑动和拖拽事件的选择
滑动和拖拽无非就是考虑是否有 “惯性” ,以及传递的参数是 “元素” 还是 “坐标”。
可以分成以下四种情况
- 有 “惯性” ,传入 “元素”
scroll
- 无 “惯性” ,传入 “元素”
drag_and_drop
- 有 “惯性” ,传入 “坐标”
swipe,并且设置较短的 duration 时间
- 无 “惯性” ,传入 “坐标”
swipe,并且设置较长的 duration 时间
小结
高级手势TouchAction
学习目标
1.能够使用代码完成轻敲手势
2.能够使用代码完成按下手势
3.能够使用代码完成抬起手势
4.能够使用代码完成等待操作
5.能够使用代码完成长按手势
6.能够使用代码完成手指移动操作
应用场景
TouchAction 可以实现一些针对手势的操作,比如滑动、长按、拖动等。我们可以将这些基本手势组合成一个相对复杂的手势。比如,我们解锁手机或者一些应用软件都有手势解锁的这种方式。
使用步骤
1.创建 TouchAction 对象
2.通过对象调用想执行的手势
3.通过 perform() 执行动作
注意点
所有手势都要通过执行perform()函数才会运行。
1.轻敲操作【掌握】- tap()
应用场景
模拟手指对某个元素或坐标按下并快速抬起。比如,固定点击(100, 100)的位置。
方法名
# 模拟手指对元素或坐标的轻敲操作
# 参数:
# element:元素
# x:x坐标
# y:y坐标
TouchAction(driver).tap(element=None, x=None, y=None).perform()
示例
打开《设置》
轻敲 “WLAN”
核心代码
el = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]")
TouchAction(driver).tap(el).perform()
2.按下和抬起操作【掌握】- press() 和release()
应用场景
模拟手指一直按下,模拟手指抬起。可以用来组合成轻敲或长按的操作
方法名
# 模拟手指对元素或坐标的按下操作
# 参数:
# el:元素
# x:x坐标
# y:y坐标
TouchAction(driver).press(el=None, x=None, y=None).perform()
# 模拟手指对元素或坐标的抬起操作
TouchAction(driver).release().perform()
示例1
使用坐标的形式按下 WLAN (650, 650),2 秒后,按下(650, 650)的位置
核心代码
TouchAction(driver).press(x=650, y=650).perform()
time.sleep(2)
TouchAction(driver).press(x=650, y=650).perform()
示例2
使用坐标的形式按下 WLAN (650, 650),2 秒后,按下(650, 650)的位置,并抬起
TouchAction(driver).press(x=650, y=650).perform()
time.sleep(2)
TouchAction(driver).press(x=650, y=650).release().perform()
3.等待操作【掌握】- wait()
应用场景
模拟手指等待,比如按下后等待 5 秒之后再抬起。
方法名
# 模拟手指暂定操作
# 参数:
# ms:暂停的毫秒数
TouchAction(driver).wait(ms=0).perform()
示例
使用坐标的形式点击 WLAN (650, 650),2 秒后,按下(650, 650)的位置,暂停 2 秒,并抬起
核心代码
TouchAction(driver).tap(x=650, y=650).perform()
time.sleep(2)
TouchAction(driver).press(x=650, y=650).wait(2000).release().perform()
4.长按操作【掌握】-long_press()
应用场景
模拟手指对元素或坐标的长按操作。比如,长按某个按钮弹出菜单。
方法名
# 模拟手指对元素或坐标的长按操作
# 参数:
# el:元素
# x:x坐标
# y:y坐标
# duration:长按时间,毫秒
TouchAction(driver).long_press(el=None, x=None, y=None, duration=1000).perform()
示例
使用坐标的形式点击 WLAN (650, 650),2 秒后,长按(650, 650)的位置持续 2 秒
核心代码
TouchAction(driver).tap(x=400, y=400).perform()
time.sleep(2)
TouchAction(driver).long_press(x=400, y=400, duration=2000).release().perform()
5.移动操作【掌握】- move_to
应用场景
模拟手指移动操作,比如,手势解锁需要先按下,再移动。
方法名
# 模拟手指对元素或坐标的移动操作
# 参数:
# el:元素
# x:x坐标
# y:y坐标
TouchAction(driver).move_to(el=None, x=None, y=None).perform()
示例
在手势解锁中,画一个如下图的案例
包名界面名为 com.android.settings/.ChooseLockPattern
核心代码
TouchAction(driver).press(x=246, y=857).move_to(x=721, y=867).move_to(x=1200,
y=851).move_to(x=1200, y=1329).move_to(x=724, y=1329).move_to(x=246,
y=1329).move_to(x=718, y=1815).release().perform()
手机操作API
学习目标
能够获取手机分辨率
能够获取手机截图
能够获取和设置网络状态
能够发送键到设备
能够打开和关闭手机通知栏
1.获取手机分辨率【掌握】
应用场景
自动化测试可能会需要根据当前设备的屏幕分辨率来计算一些点击或者滑动的坐标
方法名
# 获取手机分辨率
driver.get_window_size()
示例
输出当前设备的屏幕分辨率
核心代码
print(driver.get_window_size())
执行结果
{‘height’: 800, ‘width’: 480}
2.手机截图【掌握】
应用场景
有些自动化的操作可能没有反应,但并不报错。此时我们就可以将操作过后的关键情况,截图留存。后期也可以根据图片发现问题。
方法名
# 获取手机分辨率
# 参数:
# filename:指定路径下,指定格式的图片
get_screenshot_as_file(filename)
示例
打开设置页面
截图当前页面保存到当前目录,命名为screen.png
核心代码
driver.get_screenshot_as_file(os.getcwd() + os.sep + './screen.png')
3.获取和设置手机网络【掌握】
应用场景
视频应用在使用流量看视频的时候,大部分都会提示用户正在是否继续播放。作为测试人员,我们可能需要用自动化的形式来判断是否有对应的提示。即,用流量的时候应该有提示,不用流量的时候应该没有提示。
获取手机网络
属性名
# 获取手机网络 driver.network_connection
示例
获取当前网络类型,并打印
核心代码
print(driver.network_connection)
执行结果
6
结果对照
设置手机网络
方法名
# 设置手机网络
# 参数:
# connectionType:网络类型
driver.set_network_connection(connectionType)
示例
设置当前设备为飞行模式
核心代码
driver.set_network_connection(1)
执行效果
设备变为飞行模式
4. 发送键到设备【掌握】
应用场景
模拟按 “返回键” “home键” 等等操作,比如,很多应用有按两次返回键退出应用的功能,如果这个功能需要我们做自动化,那么一定会用到这个方法
方法名
# 发送键到设备
# 参数:
# keycode:发送给设备的关键代码
# metastate:关于被发送的关键代码的元信息,一般为默认值
driver.press_keycode(keycode, metastate=None)
注意点
按键对应的编码,可以在百度搜索关键字 “android keycode”
例如:https://blog.csdn.net/feizhixuan46789/article/details/16801429
示例
点击三次音量加,再点击返回,再点击两次音量减。
核心代码
driver.press_keycode(24)
driver.press_keycode(24)
driver.press_keycode(24)
driver.press_keycode(4)
driver.press_keycode(25)
driver.press_keycode(25)
5.操作手机通知栏【掌握】
应用场景
测试即时通信类软件的时候,如果 A 给 B 发送一条消息,B 的通知栏肯定会显示对应的消息。我们想通过通知栏来判断 B 是否收到消息,一定要先操作手机的通知栏
方法名
#打开手机通知栏
driver.open_notifications()
注意点
appium官方并没有为我们提供关闭通知的api,那么现实生活中怎么关闭,就怎样操作就行,比 如,手指从下往上滑动,或者,按返回键
示例
打开通知栏,两秒后,关闭通知栏
核心代码
driver.open_notifications()
time.sleep(2)
driver.press_keycode(4)