Appium框架

发布时间 2023-08-30 18:53:41作者: lemonHZZ

1-环境搭建

移动自动化环境搭建

1、整体思路

java
	安卓系统内应用都是有java程序开发的
Android SDK
	Android系统的开发工具包
模拟器/真机
	要测试安卓内的应用程序,必须要有安卓手机

2、java安装

安装jdk-8u151-windows-x64.exe
配置环境变量
	自定义变量JAVA_HOME=C:\Program Files\Java\jdk1.8.0_151
	注意:C:\Program Files\Java\jdk1.8.0_151 为jdk的安装目录
配置path
	%JAVA_HOME%\bin
	%JAVA_HOME%\jre\bin
验证
	终端输入:java -version
	终端输入:javac -version

3、Android SDK安装

解压android-sdk.zip到指定目录
配置环境变量
自定义变量 ANDROID_HOME=C:\tools\android-sdk
	C:\tools\android-sdk是你的解压目录
	platform-tools文件夹和tools文件夹都在C:\tools\android-sdk下
配置path
	%ANDROID_HOME%\platform-tools
	%ANDROID_HOME%\tools
验证
	终端输入:adb --version

4、安装手机模拟器

  • 模拟Genymotion安装
    1. 执行genymotion-2.11.0-vbox.exe (是一个集合程序,包含genymotion和virtualbox) -> 不需要更改配置,直接下一步默认安装
    2. 安装完genymotion继续等待,会提示安装virtualbox,继续安装,期间会提示安装oracle插件,全部允许安装
    3. 安装完成后会在桌面展示genymotion和virtualbox两个图标
      image
  • 虚拟机镜像导入
    1. 打开virtualbox
    2. 进入virtualbox -> 管理 -> 导入虚拟电脑
    3. 点击文件选择(Samsung Galaxy S6 - 5.1.0 - API 22 - 1440x2560.ova) -> 点击下一步
    4. 勾选 重新初始化所有网卡的MAC地址
    5. 点击导入按钮 -> 等待导入完成
    6. virtualbox列表会展示如下图圈出的选项
      image
  • 启动android模拟器
  1. 点击genymotion图标 -> 弹出框点击 >Personal Use
    image
  2. 勾选复选框 -> 点击Accept -> 点击跳转页面的Close按钮
    image
  3. genymotion主界面,选择对应的虚拟机,点击start按钮
    image
  4. 虚拟机启动成功
    image

5、为虚拟机提供安装apk功能

  • 说明
    安装genymotion ARM插件,此插件可提供x86运行环境,即可运行apk ⚠ 需要下载对应版本的插件(本次使用android 5.1版本插件)
  • 安装
    1. 拖动ARM_Translation_Lollipop_20160402.zip到已启动的android虚拟机上
    2. 点击提示的ok按钮,见下图
      1.拖入点击ok
      image
      2.安装成功提示如下
      image

2-adb调试工具

1、adb的工作原理

  • adb概念
ADB 全名 Android Debug Bridge,是⼀个调试⼯具。
开发安卓应⽤的程序员必须要掌握
测试⼯程师在做安卓应⽤测试时,会使⽤到
  • adb的构成和工作原理
adb包含三个部分:
1. Client端:运⾏在开发机器中,即你的开发电脑,⽤来发送 adb 命令;
2. Daemon 守护进程:运⾏在调试设备中,⼿机或模拟器,⽤来接收并执⾏ adb 命令;
3. Server端:同样运⾏在开发机器中,⽤来管理 Client 端和⼿机的 Daemon 之间的通信。

adb ⼯具可以在电脑通过终端命令操作安卓⼿机/模拟器。

image

2、adb常用命令

2.1 获取包名和界面名
  • 包名和界面名的概念
    • 包名(package):决定程序的唯一性(不是应用的名字)
    • 界面名(activity):目前可以理解,一个界面名,对应着一个界面
  • 获取包名和界面名
    • 自动化测试需要通过代码的形式告诉手机测试哪个应用程序的哪一个界面,所以需要通过这个命令进行获取。
    • 使用步骤
    1. 打开需要测试的应用程序
    2. 输入 adb 命令
    
    • 命令格式
    Mac/Linux:
    	adb shell dumpsys window windows | grep mFocusedApp
    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.2 文件传输
  • 发送文件到手机
使用场景:
	将⼿机需要的数据(数据库⽂件)在电脑上调整好,直接发送给⼿机
命令格式:
	adb push 电脑的文件路径 手机的文件夹路径
示例:将桌面的a.txt发送到手机的sd卡
	adb push C:\Users\hm\Desktop\a.txt /sdcard
  • 从手机中拉取文件
应用场景:
	将手机产⽣的⽂件(数据库⽂件,⽇志⽂件)拉取到电脑中
命令格式:
	adb pull 手机的文件路径 电脑的文件夹路径
示例:
	adb pull /sdcard/a.txt C:\Users\hm\Desktop
2.3 获取app启动时间
应用场景:
	1. 如果企业对应用程序的启动速度有要求,则需要使用这个命令进行测试
	2. 测试标准:参照同类软件,启动时间不能超出一倍即可
命令格式:
	adb shell 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 + 系统资源启动时间(毫秒)
    image
2.4 获取手机日志
应用场景:
	将bug的日志信息发送给开发⼈员,便于开发⼈员定位bug
使用步骤:
	1. 打开需要测试的应⽤程序
	2. 找到触发bug的位置
	3. 使⽤查看⽇志命令
	4. 触发bug
	5. 获取⽇志信息
命令格式:
	adb logcat
示例:
	1. 安装bug.apk
	2. 打开《有bug的程序》应⽤程序
	3. 命令⾏中输⼊ adb logcat 命令
	4. 点击登录按钮
	5. 获取⽇志信息

结果如下图:
image

2.5 其他命令

image

3-Appium介绍和安装

Appium自动化测试框架

Appium介绍

Appium是一个移动端的自动化框架,可用于测试原生应用,移动网页应用和混合型应用,且是跨平台
的。可用于iOS和Android操作系统。原生的应用是指用android或iOS的sdk编写的应用,移动网页应用
是指网页应用,类似于ios中safari应用或者Chrome应用或者类浏览器的应用。混合应用是指一种包裹
webview的应用,原生应用于网页内容交互性的应用。 重要的是Appium是跨平台的,何为跨平台,意思
就是可以针对不同的平台用一套api来编写测试脚本。

Appium自动化测试环境搭建

使用Appium和python来进行自动化测试,需要安装两个东西,一个是Appium的客户端,一个是
Appium-python库。这两个需要安装的东西在加上手机就可以进行自动化测试,它们之间的关系是:
python代码 -> Appium-python库 -> Appium -> 手机
  • Appium客户端安装
Appium桌面客户端安装方式:
	1. 运行appium-desktop-Setup-1.6.2.exe,默认安装即可
	2. 启动客户端
Appium-python库安装:
命令行安装(需联网):
	pip3 install Appium-Python-Client
可能出现的问题:
	错误提示:could not create '/usr/local/lib/python2.7/dist-packages/virtualenv_support':
Permission denied
	权限问题,使用管理员运行cmd

4-Hello Appium

快速体验

  • 应用场景:
    在做app自动化的时候,肯定是针对某个产品、某个软件进行测试,那么一定是先让模拟器或真机打开这款软件才可以。打开某个应用程序。
  • 需求
    使用一下步骤可打开模拟器中的设置应用程序
    步骤:
    1.打开手机模拟器
    2.打开appium工具
    3.创建一个python项目,取名为 hello_appium
    4. 创建一个 demo.py 文件
    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 = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
    driver.quit()
    

参数详解

应用场景:
	如果后期项目不是测试《设置》应用程序,而是测试《短信》应用程序那么怎么打开《短信》应用程序呢?如果后期项目测试的模拟器或手机不再是5.1的版本,而是6.1的版本呢?相关配置的信息在学习之后都可以进行修改。

参数解释

# 导模块
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端口开启了一个服务
1. 我们写的 python 代码会访问本机的 appium 服务器,并获取 driver 对象
2. appium 会将我们的 driver 对象调用的方法转化成 post 请求,提交给appium服务器
3. appium 通过接收到的 post 请求发送给手机,再由手机进行执行

5-Appium基础操作API

前置代码

from appium import webdriver

desired_caps=dict()
#手机参数
desired_caps['platformName']='Andriod'
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:要打开的程序的界面名
	diver.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.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 会在进入后台一定时间后,如果再回到前台也页面会重新输入密码,如果需要自动化测试这种功能,可以使用这个 api 进行测试
方法:
	#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)
driver.background_app(5)
time.sleep(3)
# 退出driver
driver.quit()
  • 热启动:表示进入后台回到前台。关机再开这种切断电源的行为可以叫做 ”冷启动“

6-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“ 信息

image

image

注意点

  1. 自动打开的命令行窗口不要关
    • 如果关了,整个工具也会关闭
  2. 打开uiautomatorviewer闪退
    • 解决方案:
      • jdk版本问题造成的,jdk为1.9时可能会出现这个问题,请换成1.8的版本
  3. 点击第二个按钮报错
    image
    • 解决方案:
      • adb kill-server
      • adb start-server

7-元素定位操作API

  • 应用场景
    需要通过元素定位来获取元素,才能让计算机帮我们 ”操作“ 这个元素。
  • 步骤
    1. 打开 uiautomatorviewer 工具
    2. 打开模拟器或真机
    3. 通过 uiautomatorviewer 工具获取想要进行操作的元素的 Node Detail 信息
    4. 通过元素定位 API 进行定位
    5. 对元素进行相关操作
  • 注意点
    元素的定位基于当前屏幕范围内展示的可见元素

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 的形式,定位 ”返回“ 按钮,并点击
driver.find_element_by_id("com.android.settings:id/search").click()
driver.find_element_by_class_name("android.widget.EditText").send_keys("hello")
driver.find_element_by_xpath("//*[@content-desc='收起']").click()
  • 小结
  1. find_element_by_id 方法中传入的是 Node Detail 信息中的 resource-id
  2. find_element_by_class_name 方法中传入的是 Node Detail 信息中的 class
  3. find_element_by_xpath 方法中传入的是 Node Detail 信息中的 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 的形式,获取所有包含 ”设“ 的元素,并打印其文字内容
  • 关键代码
titles=driver.find_elements_by_id(""com.android.settings:id/title")
for title in titles:
	print(title.text)
text_views=driver.find_elements_by_class_name(""android.widget.TextView")
for text_view in text_views:
	print(text_view.text)
elements = driver.find_elements_by_xpath("//*[contains(@text,'设')]")
for element in elements:
	print(element.text)

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 方法,如果传入一个没有的特征,不会报错,会返回一个空列表

8-元素等待

  • 应用场景
    可能由于一些原因,想找的元素并没有立刻出来,此时如果直接定位可能会报错,比如以下原因:
    1. 由于网络速度原因
    2. 服务器处理请求原因
    3. 电脑配置原因
  • 概念
    WebDriver定位页面元素时如果未找到,会在指定时间内一直等待的过程
    元素等待一共分为两种类型
    1. 显式等待
    2. 隐式等待

1、隐式等待

  • 应用场景
    针对所有定位元素的超时时间设置为同一个值的时候
  • 概念
    等待元素加载指定的时长,超出时长抛出NoSuchElementException异常
  • 步骤
    1. 在获取 driver 对象后,使用 driver 调用 implicitly_wait 方法即可
  • 示例
    在 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)
    

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)

3、隐式等待和显式等待的选择

  • 作用域:
    显式等待为单个元素有效,隐式为全局元素
  • 方法:
    显式等待方法封装在 WebDriverWait 类中,而隐式等待则直接通过 driver 实例化对象调用
  • 关于sleep的形式
    sleep 是固定死一个时间,不是不行,是不推荐。
    元素等待可以让元素出来的第一时间进行操作。sleep 可能造成不必要的浪费。

9-元素操作API

1、点击元素

  • 应用场景
    需要点击某个按钮的时候使用
  • 方法名
    #对element按钮进行点击操作
    element.click()
    
  • 示例
    1. 打开《设置》
    2. 点击放大镜按钮
    driver.find_element_by_id("com.android.settings:id/search").click()
    

2、输入和清空输入框内容

  • 应用场景
    需要对输入框进行输入或亲空的时候使用
  • 方法名
    # 对element输入框进行输入操作
    # 参数:
    # value:输入的内容
    element.send_keys(value)
    #清空
    element.clear()
    
  • 示例
    1. 打开《设置》
    2. 点击 ”放大镜“
    3. 输入 ”hello“
    4. 暂停 2 秒
    5. 清空所有文本内容
    6. 暂停 5 秒
    7. 输入 ”你好“
    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("你好")
    
  • 注意点
    默认输入中文无效,但不会报错,需要在 ”前置代码“ 中增加两个参数
    desired_caps['unicodeKeyboard']=True
    desired_caps['resetKeyboard']=True
    

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
    
  • 示例
    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"))
    	print(title.get_attribute("resourceId"))
    	print(title.get_attribute("ClassName"))
    
  • 注意点
    value='text'返回text的属性值
    value='name'返回content-desc/text的属性值
    value='className'返回class的属性值,只有API=>18才能支持
    value='resourceId'返回resource-id的属性值,只有API=>18才能支持
    

10-滑动和拖拽事件

1、swipe滑动事件

  • 概念
    从一个坐标位置滑动到另一个坐标位置,只能是两个点之间的滑动
  • 方法名
    # 从一个坐标位置滑动到另一个坐标位置,只能是两个点之间的滑动
    # 参数:
    # 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)
    
  • 示例
    模拟手指从(100, 2000),滑动到(100, 1000)的位置
    driver.swipe(100,2000,100,1000)
    模拟手指从(100, 2000),滑动到(100, 100)的位置
    driver.swipe(100,2000,100,100)
    模拟手指从(100, 2000),滑动到(100, 100)的位置,持续5秒
    driver.swipe(100,2000,100,100,5000)
    
  • 小结
    距离相同时,持续时间越长,惯性越小
    持续时间相同时,手指滑动的距离越大,实际滑动的距离也就越大

2、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_btton,more_button)
    
  • 小结
    不能设置持续时间,惯性很大

3、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)
    
  • 小结
    不能设置持续时间,没有惯性

4、滑动和拖拽事件的选择

  • 滑动和拖拽无非就是考虑是否具有“惯性”,以及传递的参数是“元素”还是“坐标”
  • 可以分为以下四种情况
    • 有“惯性”,传入“元素”
      • scroll
    • 无“惯性”,传入“元素”
      • drag_and_drop
    • 有“惯性”,传入“坐标”
      • swipe,并且设置较短的duration时间
    • 无“惯性”,传入“坐标”
      • swipe,并且设置较长的duration时间

11-高级手势TouchAction

  • 应用场景
    TouchAction 可以实现一些针对手势的操作,比如滑动、长按、拖动等。我们可以将这些基本手势组合成一个相对复杂的手势。比如,我们解锁手机或者一些应用软件都有手势解锁的这种方式。
  • 使用步骤
    1. 创建 TouchAction 对象
    2. 通过对象调用想执行的手势
    3. 通过 perform() 执行动作
  • 注意点
    所有手势都要通过执行perform()函数才会运行。

1、轻敲操作

  • 应用场景
    模拟手指对某个元素或坐标按下并快速抬起。比如,固定点击(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、按下和抬起操作

  • 应用场景
    模拟手指一直按下,模拟手指抬起。可以用来组合成轻敲或长按的操作
  • 方法名
    # 模拟手指对元素或坐标的按下操作
    # 参数:
    # el:元素
    # x:x坐标
    # y:y坐标
    TouchAction(driver).press(el=None,x=None,y=None).perform()
    # 模拟手指对元素或坐标的抬起操作
    TouchAction(driver).release().perform()
    
  • 示例
    使用坐标的形式按下 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()
    使用坐标的形式按下 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、等待操作

  • 应用场景
    模拟手指等待,比如按下后等待 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、长按操作

  • 应用场景
    模拟手指对元素或坐标的长按操作。比如,长按某个按钮弹出菜单。
  • 方法名
    # 模拟手指对元素或坐标的长按操作
    # 参数:
    # 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、移动操作

  • 应用场景
    模拟手指移动操作,比如,手势解锁需要先按下,再移动。
  • 方法名
    # 模拟手指对元素或坐标的移动操作
    # 参数:
    # el:元素
    # x:x坐标
    # y:y坐标
    TouchAction(driver).move_to(el=None,x=None,y=None).perform()
    
  • 示例
    在手势解锁中,画一个如下图的案例
    包名界面名为 com.android.settings/.ChooseLockPattern
    image
    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()
    

12-手机操作API

1、获取手机分辨率

  • 应用场景
    自动化测试可能会需要根据当前设备的屏幕分辨率来计算一些点击或者滑动的坐标
  • 方法名
    # 获取手机分辨率
    driver.get_window_size()
    
  • 示例
    #输出当前设备的屏幕分辨率
    print(driver.get_window_size())
    #执行结果
    {'height': 800, 'width': 480}
    

2、手机截图

  • 应用场景
    有些自动化的操作可能没有反应,但并不报错。此时就可以将操作过后的关键情况,截图留存。后期也可以根据图片发现问题。
  • 方法名
    # 参数:
    # filename:指定路径下,指定格式的图片
    get_screenshot_as_file(filename)
    
  • 示例
    1. 打开设置页面
    2. 截图当前页面保存到当前目录,命名为screen.png
    driver.get_screenshot_as_file(os.getcwd()+os.sep+'./screen.png')
    执行效果
    项目目录下会将设置页面保存成 screen.png
    

3、获取和设置手机网络

  • 应用场景
    视频应用在使用流量看视频的时候,大部分都会提示用户正在是否继续播放。需要用自动化的形式来判断是否有对应的提示。即,用流量的时候应该有提示,不用流量的时候应该没有提示。

1.获取手机网络

```
#属性名
#获取手机网络
driver.network_connection
```
  • 示例
    获取当前网络类型,并打印
    print(driver.network_connection)
    执行结果
    6
    
  • 结果对照
    image

2.设置手机网络

  • 方法名
    # 设置手机网络
    # 参数:
    # 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)