Appium构成和工作原理
前言:每一篇博客我都想写一个前言,第一是明确我对写这篇博客得态度,必须要认真和用心,另外作为技术分享,我也希望能帮助到一些伙伴有一些技术性得提升,测试 “点点点” 得时代已经结束,毕竟现在是智能化、自动化的时代,我们也必须要用技术武装自己,这样才能不被时代抛弃(明明可以靠脸吃饭的我,硬是吃成了一个胖子),那么下面我们言归正传,介绍一下Appium的基本使用,以前有些同事想搞这个但是止步于配置,你呢?
安装java环境和ANDROID-SDK
jdk和android-sdk文件下载地址:
链接:https://pan.baidu.com/s/1PEIfAbVwTR5mdL-6Ohca2g
提取码:3bmu
安装步骤:
- 下载后安装JAVA
- 配置JAVA环境变量
添加 JAVA_HOM 变量
变量值为JDK在你电脑上的安装路径例如:
C:\Program Files\Java\jdk1.8.0
创建好后则可以利用%JAVA_HOME%作为JDK安装目录的统一引用路径。
打开 PATH 后设置 bin目录
%JAVA_HOM%\bin
- 安装 android-sdk 并设置环境变量
添加 ANDROID_SDK 变量
变量值为JDK在你电脑上的安装路径例如:
C:\Program Files\android\android-sdk-windows
创建好后则可以利用%JAVA_HOME%作为JDK安装目录的统一引用路径。
打开 PATH 后设置 bin目录
%ANDROID_SDK%\platform-tools
%ANDROID_SDK%\tools
-
检查是否安装成功,打开cmd,分别输入java -version,adb 不报错则配置正确
-
adb构成
- client端, 在电脑上,负责发送adb命令
- daemon守护进程,在手机上,负责接收和执行adb龠令
- server端, 在电脑上,负责管理client和daemon之间的通信
-
adb工作原理
- client遥将命令发送给server端
- server端会将命令发送给daemon端
- daemon端进行执行
- 将执行结果,返回给server端
- server端将结果再返回给client端
安装Appium(配合夜游模拟器使用)
- 安装python环境
- 下载并安装appium客户端和夜游模拟器(下载后分别双击安装即可)
链接:https://pan.baidu.com/s/1tGlQLZbwCSCTYptgetYRwg
提取码:5dpf
-
Appium-python 库安装
pip install Appium-Python-Client
-
appium和夜游模拟器的连接
-
开启appium
-
打开夜神模拟器开发者模式
4.2.1 打开夜神模拟器,打开设置,调成手机模式,初次进入的话,进入设置, 点击版本号5次,可以激活使用开发者模式,进入后打开USB调试功能 4.2.2 打开电脑文件资源管理器,进入夜神模拟器的安装位置,在地址栏输入cmd, 回车,会打开cmd窗口,进入的路径就是夜神模拟器的安装位置。我的默认安装位置: C:\Program Files\Nox\bin 4.2.3 然后命令行窗口中输入nox_adb.exe connect 127.0.0.1:62001 即可以连接到adb 4.2.4 完成上述步骤后,此时启动appium便可以开始执行测试脚本了
-
adb 常用命令
获取设置程序的 包名 和 界面名
1.包名:决定程序的唯一性
2.界面名:程序中的每一个界面,对应一个界面名称
- windows:
adb shell dumpsys window windows | findstr mFocusedApp
- mac | Linux
adb shell dumpsys window windows | grep mFocusedApp
这里关于adb命令不做过多说明,个人经验多记就好,不是难点。
Appium使用(配合夜游模拟器使用)
Appium启动过程介绍
- appiun的启动实际上是在本机使用了4723端口开启了一个服务
我们写的python代码会访问本机的appium服务器,并获取driver对象 - appium会将我们的driver对象调用的方法转化成post请求,提交给appium服务器
- appium 通过接收到的post请求发送给手机,再由手机进行执行
Appium的基础操作API(基本目标)
- 能够使用appium在脚本内启动其他app
# 启动短信的应用程序(跳转其他应用)
# app_package:要跳转的应用程序名
# app_activity:要跳转的页面名
driver.start_activity("com.android.contacts",".activities.PeopleActivity")
- 能够使用appium获取包名和界面名
#获取当前的程序的包名和界面名
page_name = driver.current_package
activity_name = driver.current_activity
print(page_name,activity_name)
- 能够使用appium关闭app和驱动对象(可以通过如下代码对比)
driver.quit():直接关闭驱动对象,退出会话
driver.close_app():退出当前App回到主界面
#退出当前App回到主界面(com.vphone.launcher)
driver.close_app()
print(driver.current_package)
time.sleep(4)
#直接关闭驱动对象
driver.quit()
print(driver) #driver对象存在,
print(driver.current_package) #当执行到这行代码时,异常(A session is either terminated or not started)
- 能够使用appium安装和卸载app
driver.is_app_installed(): 判断是否安装了程序
driver.remove_app():卸载安装程序
driver.install_app():安装本地程序包
isinstall = driver.is_app_installed("com.ss.android.ugc.aweme")
if (isinstall):
print("程序已安装,卸载程序")
driver.remove_app("com.ss.android.ugc.aweme")
else:
print("程序未安装,安装程序")
driver.install_app(
"C:/Users/Administrator/Desktop/Downloads/aweme_aweGW_v11.2.0_97887f5.apk"
)
- 能够使用appium将应用置于后台
driver.background_app()
#该方法往往用来测试程序的热启动,进入后台后,等待一段时间,自动回到前台
#seconds:单位秒
driver.background_app(10)
从页面中获取元素信息(UIAutomatorViewer)
主要作用:定位元素的时候必须根据元素的相关特征来进行定位,而我们的UlutomnatorVewer就是用来获取元素特征的,概括来说就是用来扫描和分析
Android应用程序的uI控件的工具。
使用步骤:
-
step1:打开UIAutomatorViewer
- windows:在自己安装的android-sdk-windows的tools目录下打开uiautomatorviewer.bat
- mac: 同样在android-sdk-windows的tools/bin目录下打开uiautomatorviewer
-
step2:打开待测试app要获取元素的页面(真机或者模拟器)
-
step3:使用uiautomatorviewer获取元素(抖音为例)
元素定位操作API(常用操作)
要求:熟悉HTML,CSS
能够分别使用id、class、 xpath定位某一个元素
能够分别使用id、class、 xpath定位某-组元素
提示:所定位的元素,只能是基于界面内的可见元素
定位一个或者一组元素
- 根据id定位元素
driver.find_element_by_id()
driver.find_elements_by_id()
- 根据class定位元素
driver.find_element_by_class_name()
driver.find_elements_by_class_name()
- 根据class定位元素
driver.find_element_by_xpath("//[@content-desc=’ ']")
driver.find_elements_by_xpath("//[@content-desc=’ ']")
注意:
- find_element_by_xxx 如果出现了多个id、class相同的元素,以上方法只会定位到第一个,find_elements_by_xxx 会定位一组
元素等待
工作场景: 由于一些原因,我们想找的元素并没有立刻出来,此时如果直接定位可能合报销,比如以下原因
- 由于网络速度原因
- 服务器处理请求原因
- 电脑配置原因
-
隐式等待
-
说明:等待元素加载指定的时长,超出时长抛出NoSuchElementException异常
-
使用:在获取driver对象后,使用driver调用implcity _wait方法即可
# 在设置了超时时间之后,后续所有的定位元素的方法都会在这个时间内等待元素的出现。 # 如果在设定时间出现了,直接进行后续操作。 # 如果在设定时间没有出现,则报错,NoSuchElementException driver.implicitly_wait(3)
-
-
显示等待
-
说明:当定位元素的超时时间设置为不同的值的时候,等待元素加载指定的时长,超出时长抛出TimeoutException异常
-
使用:WebDriverWait(driver,超时时长,调用频率).until(lambda x:x.find_element_by_id(" "))
# 在5秒内,每一秒钟找一次id为:"com.ss.android.ugc.aweme:id/c3k" 得元素 # 如果在设定时间出现了,直接进行后续操作。 # 如果在设定时间没有出现,则报错,NoSuchElementException # 只针对于当前寻找元素有效 button = WebDriverWait(driver,5,1).until(lambda x:x.find_element_by_id("com.ss.android.ugc.aweme:id/c3k")) button.click()
-
经验总结: 因为显示和隐式等待得作用域不同,需要分具体情况来使用,如果要单独设置某一个元素得等待时间则使用显示等待,如果要设置所有元素得等待时间,则使用隐式等待,不推荐使用time.sleep()这种强制等待得方式
元素操作API
1.使用代码点击按钮(前提定位到该元素,并且该元素可点击)
element.click() # element:为定位的元素
2.使用代码对输入框输入文字(前提定位到该元素,并且该元素是文本框)
element.send_keys(value) # value:为输入文本的内容 ;element:为定位的元素
3.使用代码对输入框清空文字 (前提定位到该元素)
element.clear() # element:为定位的元素
4.使用代码获取元素的文本内容(返回数据类型为String)
element.text #element:为定位的元素
5.能够使用代码获取元素的位置和大小(返回数据类型为dict)
element.location # element:为定位的元素
element.size # element:为定位的元素
6.能够使用代码根据属性名获政元素的属性值
根据特征定位到元素后,使元素的属性名获取对应的属性值
使用get_attribute可以获取这些元素的enabled、text、content-desc、
resource-id、class 等的属性值
注意:
。想要获取resource id使用resourceId属性名API >= 18
。想要获取class使用ClassName属性名API >= 18
。想要获取content-desc使用 name 属性名
。其他的属性,都可以参考uiautomator viewer中的属性名
element.get_attribute(“enabled”)
element.get_attribute(“text”)
element.get_attribute(“name”)
element.get_attribute(“resourceId”)
element.get_attribute(“ClassName”)
页面的滑动和拖拽事件
工作场景:
自动化测试的时候,有些按钮是需要滑动几次屏幕后才会出现,
此时,需要模拟手指的滑动,这里就用到了滑动和拖拽事件
- 使用swpe滑动屏幕
- 使用scroll滑动屏幕
- 使用drag_and_drop滑动屏幕
swpe滑动屏幕
说明:从一个坐标位置滑动到另一个坐标位置,只能是两个点之间的滑动。
driver.swipe(start_x,start_y,end_x,end_y,duration)
"""
Args:
start_x:(起始位置x坐标) x-coordinate at which to start
start_y:(起始位置Y坐标) y-coordinate at which to start
end_x:(结束位置x坐标) x-coordinate at which to stop
end_y:(结束位置y坐标) y-coordinate at which to stop
duration: (滑动持续的时间,单位毫秒)time to take the swipe, in ms.
Usage:
driver.swipe(100, 100, 100, 400)
"""
driver.swipe(100,50,100,2000,3000)
注意:
- 使用该方法如果start_y和end_y的y坐标差值太大,会出现偏差(因为惯性),如果坐标差值太大建议使用duration参数缓冲(持续时间越长,惯性越弱)。
- 当 start_y 小于 end_y 表示向上拉
- 当 start_y 大于 end_y 表示向下拉
scroll滑动屏幕
说明:从一个元素滑动到另一个元素,直到页面自动停止。
driver.scroll(origin_el,destination_el,duration)
"""
Args:
origin_el: 滑动的起始元素
destination_el: 滑动的结束元素
duration: 持续时间单位秒,default:600ms
Usage:
driver.scroll(el1, el2)
"""
driver.scroll(start_element,end_element)
drag_and_drop滑动屏幕
说明:从一个元素滑动到另一个元素,第二个元素替代第一个元素原本屏幕上的位置
driver.drag_and_drop(origin_el,origin_el)
"""
Args:
origin_el: 起始元素
destination_el: 目标元素
"""
driver.drag_and_drop(origin_el,origin_el)
TouchAction常用手势操作
工作应用场景:
TouchAction可以实现一些针对手势的操作,比如滑动。长按、拖动等等。
工作中可能需要一些复杂的手势,就可以将这些基本手势组合成一个相对复杂的手势。
- 使用代码完成轻敲手势
- 使用代码完成按下手势
- 使用代码完成抬起手势
- 使用代码完成等待操作
- 使用代码完成长按手势
- 使用代码完成手指移动操作
使用流程
- 创建TouchAction时象
- 通过对象调用想执行的手势
- 通过perform)执行动作
使用代码完成轻敲手势
- 说明:模拟手指对某个元素或坐标****按下并快速抬起。比如,固定点击(150, 200)的位置。
TouchAction(driver).tap( element , x , y ).perform()
"""
element: 要轻敲的元素
x : 相对于元素的左上角,x坐标点击
y : y坐标。如果使用y,也必须设置x。
"""
# 根据元素完成轻敲
tap_element = driver.find_element_by_id("")
TouchAction(driver).tap(element=tap_element).perform()
# 根据坐标完成轻敲,敲击位置(70,100)
TouchAction(driver).tap(x=70,y=100).perform()
注意:如果你认真听,会发现,敲击的过程会发出 “咚” 的声音。
使用代码完成按下、长按、等待、抬起手势
说明:模拟手指- 直按下,模拟手指抬起。可以用来组合成轻敲或长按的操作
TouchAction(driver).press().perform()
TouchAction(driver).release().perform()
TouchAction(driver).long_press().perform()
TouchAction(driver).press().wait().release().perform() #等价于第三种方式
press_element = driver.find_element_by_id("com.ss.android.ugc.aweme:id/emz")
# 按下或者抬起
"""
el: the element to press
x: x coordiate to press. If y is used, x must also be set
y: y coordiate to press. If x is used, y must also be set
pressure: 仅仅iOS端使用
"""
#根据元素长按
TouchAction(driver).press(el=press_element).perform()
#根据坐标长按
TouchAction(driver).press(x=70,y=100).perform()
"""
el: the element to press(要按的元素对象)
x: x坐标,x存在,则y必须存在
y: y坐标,y存在,则x必须存在
duration: 长按持续时间(毫秒)
"""
#根据元素长按,并抬起
TouchAction(driver).press(el=press_element).release().perform()
#根据元素长按持续时间为1秒,并抬起
TouchAction(driver).long_press(el=press_element,duration=1000).release().perform()
#按下等待一秒然后抬起
"""
wait method
Args:
ms: 暂停,持续时间
"""
TouchAction(driver).press(el=press_element).wait(1000).release().perform()
使用代码完成手指移动操作
使用场景:在开发过程种有些功能是用户私有的,可以根据用户习惯设置密码(手势密码)
说明:模拟手指移动操作,比如,手势解锁需要先按下,再移动。
TouchAction(driver).press(x=146,y=523).move_to(x=449,y=523).perform() #按下位置146,523,移动到449,523
"""
Move the pointer from the previous point to the element or point specified
从一个点移动到另一个点(或元素)
Args:
el: 要移动到的元素(element)
x: 要移动到的位置的x坐标,x存在,则y必须存在
y: 要移动到的位置的y坐标,y存在,则x必须存在
"""
TouchAction(driver).press(x=146,y=523).move_to(x=449,y=523).move_to(x=750,y=523).move_to(x=447,y=822).move_to(x=150,y=1125).move_to(x=445,y=1125).move_to(x=750,y=1125).release().perform()
其他(手机操作API)
- 获取手机分所率
driver.get_window_size()
打印结果如下:
{'width': 900, 'height': 1600}
- 获取手机截屏
driver.get_screenshot_as_file("设置为要保存的文件路径")
-
获取和设置网络状态
应用场景: 现在一般的手机应用在使用流量看视频的时候,大部分都会提示用户正在是否继续播放。作为测试,我们可能需要用自动化的形式来判断是否有对应的提示。即用流量的时候应该有提示,不用流量的时候应该没有提示(一般情况下)。3.1 获取当前网络状态
driver.network_connection
注意:返回值为int类型的数字个数字含义如下(0表示关闭,1表示开启)
3.2 设置手机网络状态""" Args: connection_type: int 类型 appium.webdriver.ConnectionType 的枚举类型 class ConnectionType: NO_CONNECTION = 0 AIRPLANE_MODE = 1 WIFI_ONLY = 2 DATA_ONLY = 4 ALL_NETWORK_ON = 6 """ # 例如设置当前网络类型为AIRPLANE_MODE result = driver.set_network_connection(1)
3.3 判断当前手机网络状态
from appium.webdriver.connectiontype import ConnectionType if driver.network_connection == ConnectionType.WIFI_ONLY: print("WIFI_ONLY,只开启了WIFI") elif driver.network_connection == ConnectionType.NO_CONNECTION: print("没有网络") elif driver.network_connection == ConnectionType.NO_CONNECTION: print("飞行模式") elif driver.network_connection == ConnectionType.DATA_ONLY: print("使用流量") else: print("流量和WIFI均已开启")
-
home键或者说是返回键(发送键到设备)
说明:模拟按“返回键" 、home键等等操作,例如很多应用有按两次返回键退出应用的功能,这个功能需要我们做
自动化就需要使用该功能""" Args: keycode: the keycode to be sent to the device metastate: meta information about the keycode being sent """ #音量+ driver.press_keycode(keycode=24) #返回 driver.press_keycode(keycode=4)
关于keycode:https://www.cnblogs.com/yc-c/p/9014771.html
-
能够打开和关闭手机通知栏
driver.open_notifications() time.sleep(2) driver.press_keycode(4) time.sleep(1)
总结:最后无论这篇分享是否能帮到你,都希望路过的你能有哪怕零点一分的收获,我花费的时间也就没有浪费,最后献上通俗易懂的代码:
下载地址: https://github.com/ljhyigehaoren/Appium_demo.git