启动代码(前置代码)
# 导模块
from appium import webdriver
# 创建一个字典,包装相应的启动参数
desired_caps = dict()
# 需要连接的手机的平台(不限制大小写)
desired_caps['platformName'] = 'Android'
# 需要连接的手机的版本号(比如 9.2.1 的版可以填写9.2.1或9.2或9,以此类推)
desired_caps['platformVersion'] = '9'
# 需要连接的手机的设备号(andoird平台下,可以随便写,但是不能不写)
desired_caps['deviceName'] = '192.168.56.101:5555'
# 需要启动的程序的包名
desired_caps['appPackage'] = '启动的包名'
# 需要启动的程序的界面名
desired_caps['appActivity'] = '启动的界面名'
# 文本框输入中文时要用
desired_caps['unicodeKeyboard'] = True
desired_caps['resetKeyboard'] = True
# 连接appium服务器,获取driver
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
# 退出driver
driver.quit()
启动过程:
注:appium的启动实际上是在本机使用了4723端口开启了一个服务
- python 代码会访问本机的 appium 服务器,并获取 driver 对象
- appium 会将我们的 driver 对象调用的方法转化成 post 请求,提交给appium服务器
- appium 通过接收到的 post 请求发送给手机,再由手机进行执行
1、在脚本内启动其他 app
应用场景:如果一个应用需要跳转到另外一个应用,就可以使用这个 api 进行应用的跳转,就像我们通过外卖应用下订单之后会跳转到支付应用一样
方法:
# 参数:
# appPackage:要打开的程序的包名
# appActivity:要打开的程序的界面名
driver.start_activity(appPackage, appActivity)
2、获取 app 的包名和界面名
应用场景:当我们从一个应用跳转到另外一个应用的时候,想输出其包名、界面名或者想在报告中展现对应信息, 我们就可以调用这个属性来进行获取
方法:
# 获取包名
driver.current_package
# 获取界面名
driver.current_activity
3、关闭 app 和驱动对象
应用场景:有的时候我们需要关闭某个应用程序后,再打开新的应用
方法:
# 关闭当前操作的app,不会关闭驱动对象
driver.close_app()
# 关闭驱动对象,同时关闭所有关联的app
driver.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)
5、将应用置于后台
应用场景:银行类 app 会在进入后台一定时间后,如果再回到前台也页面会重新输入密码,如果需要自动化测试这种功能,可以使用这个 api 进行测试
方法:
# app放置到后台一定时间后再回到前台,模拟热启动
# 参数:
# seconds:后台停留多少秒
driver.background_app(seconds)
热启动:表示进入后台回到前台。关机再开这种切断电源的行为可以叫做“冷启动”
6、元素定位
应用场景:需要通过元素定位来获取元素,才能让计算机帮我们 ”操作“ 这个元素
元素属性值获取:通过安装的Android SDK目录下的uiautomatorviewer工具可以获取
定位一个元素方法:
- 通过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定位一组元素
# 参数:
# 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)
注意点:
1、元素的定位基于当前屏幕范围内展示的可见元素。
2、如果使用 find_element_by_xx 方法,如果传入一个没有的特征,会报NoSuchElementException 的错误。
3、如果使用 find_elements_by_xx 方法,如果传入一个没有的特征,不会报错,会返回一个空列表。
7、元素等待
应用场景:可能由于一些网络速度原因、服务器处理请求、电脑配置原因等等,我们想找的元素并没有立刻出来,此时如果直接定位可能会报错
分类:隐式等待和显示等待
(1)隐式等待
作用范围:针对所有定位元素的操作(全局有效)
方法:
# 参数:
# timeout:超时的时长,单位:秒
implicitly_wait(timeout)
(2)显示等待
作用范围:针对某一个定位元素的操作(单个有效)
方法:
# 导入显示等待包
from selenium.webdriver.support.wait import WebDriverWait
# 参数:
# driver:驱动对象
# timeout:超时的时长,单位:秒
# poll_frequency:检测间隔时间,默认为0.5秒
# method:lambda查找元素表达式
# 返回值:
# 定位到的元素,如果没有定位到会抛出TimeoutException异常
element = WebDriverWait(driver, timeout, poll_frequency=0.5).until(method)
8、点击元素
应用场景:需要点击某个按钮的时候使用
方法:
# 对element按钮进行点击操作
element.click()
9、输入和清空输入框内容
应用场景:需要对输入框进行输入或清空的时候使用
方法:
# 对element输入框进行输入操作
# 参数:
# value:输入的内容
element.send_keys(value)
# 对element输入框进行输入操作
element.clear()
注意点: 默认输入中文无效,但不会报错,需要在 ”前置代码“ 中有着这两个参数
desired_caps['unicodeKeyboard'] = True
desired_caps['resetKeyboard'] = True
10、获取元素的文本内容
应用场景:需要获取按钮、文本框、输入框等控件的文本内容时使用
方法:
# 获取element控件的文本内容
# 返回值:
# 控件的文本内容
element.text
11、获取元素的位置和大小
应用场景:需要获取元素的位置和大小的时候使用
方法:
# 获取element的位置
# 返回值:
# 字典,x为元素的x坐标,y为元素的y坐标
element.location
# 获取element的大小
# 返回值:
# 字典,width为宽度,height为告诉
element.size
12、获取元素的属性值
应用场景:根据特征定位到元素后,使元素的属性名获取对应的属性值
方法:
# 获取element的属性值
# 参数:
# value:要获取的属性名
# 返回值:
# 根据属性名得到的属性值
element.get_attribute(value) # value:元素的属性
注意点:
value='text' 返回text的属性值
value='name' 返回content-desc / text属性值
value='className' 返回 class属性值,只有 API=>18 才能支持
value='resourceId' 返回 resource-id属性值,只有 API=>18 才能支持
13、滑动和拖拽事件
应用场景:有些按钮是需要滑动几次屏幕后才会出现,此时我们需要使用代码来模拟手指的滑动
分类:swipe滑动事件、scroll滑动事件、drag_and_drop拖拽事件
(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)
距离相同时,持续时间越长,惯性越小
持续时间相同时,手指滑动的距离越大,实际滑动的距离也就越大
(2)scroll滑动事件
概念:从一个元素滑动到另一个元素,直到页面自动停止(惯性很大)
方法:
# 参数:
# origin_el: 滑动开始的元素
# destination_el: 滑动结束的元素
driver.scroll(origin_el, destination_el)
(3)drag_and_drop拖拽事件
概念:从一个元素滑动到另一个元素,第二个元素替代第一个元素原本屏幕上的位置(无惯性)
方法:
# 参数:
# origin_el: 滑动开始的元素
# destination_el: 滑动结束的元素
driver.drag_and_drop(origin_el, destination_el)
14、高级手势TouchAction
应用场景:TouchAction 可以实现一些针对手势的操作,比如滑动、长按、拖动等。我们可以将这些基本手势组合 成一个相对复杂的手势。比如,我们解锁手机或者一些应用软件都有手势解锁的这种方式。
使用步骤:
- 1. 创建 TouchAction 对象
- 2. 通过对象调用想执行的手势
- 3. 通过 perform() 执行动作
(1)轻敲操作
概念:模拟手指对某个元素或坐标按下并快速抬起
方法:
# 参数:
# element:元素
# x:x坐标
# y:y坐标
TouchAction(driver).tap(element=None, x=None, y=None).perform()
注:元素和坐标任选其一,选择坐标时需要标明x=,y=,否则会被误认为是element
(2)按下和抬起操作
概念:模拟手指一直按下,模拟手指抬起。可以用来组合成轻敲或长按的操作
方法:
# 模拟手指对元素或坐标的按下操作
# 参数:
# el:元素
# x:x坐标
# y:y坐标
TouchAction(driver).press(el=None, x=None, y=None).perform()
# 模拟手指对元素或坐标的抬起操作
TouchAction(driver).release().perform()
(3)等待操作
概念:模拟手指等待,比如按下后等待 5 秒之后再抬起
方法:
# 参数:
# ms:暂停的毫秒数
TouchAction(driver).wait(ms=0).perform()
(4)长按操作
概念:模拟手指对元素或坐标的长按操作。比如,长按某个按钮弹出菜单
方法:
# 参数:
# el:元素
# x:x坐标
# y:y坐标
# duration:长按时间,毫秒
TouchAction(driver).long_press(el=None, x=None, y=None, duration=1000).perform()
(5)移动操作
概念:模拟手指移动操作,比如,手势解锁需要先按下,再移动
方法:
# 参数:
# el:元素
# x:x坐标
# y:y坐标
TouchAction(driver).move_to(el=None, x=None, y=None).perform()
15、获取手机分辨率
应用场景:需要根据当前设备的屏幕分辨率来计算一些点击或者滑动的坐标
方法:
driver.get_window_size() # 返回值是字典{'height'= x, 'width'= x}
16、获取手机截图
应用场景:有些自动化的操作可能没有反应,但并不报错。此时我们就可以将操作过后的关键情况,截图留存。后期也可以根据图片发现问题
方法:
# 参数:
# filename:指定路径下,指定格式的图片
get_screenshot_as_file(filename)
17、获取和设置手机网络
应用场景:视频应用在使用流量看视频的时候,大部分都会提示用户正在是否继续播放。我们可能需要用自动化的形式来判断是否有对应的提示。即用流量的时候应该有提示,不用流量的时候应该没有提示。
方法:
# 获取手机网络
driver.network_connection
# 设置手机网络
# 参数: # connectionType:网络类型
driver.set_network_connection(connectionType)
手机网络类型对照: (流量、WiFi、飞行模式)
18、发送键到设备
应用场景:模拟按 “返回键” “home键” “音量加减”等等操作
方法:
# 参数:
# keycode:发送给设备的关键代码
# metastate:关于被发送的关键代码的元信息,一般为默认值
driver.press_keycode(keycode, metastate=None)
常用的手机键:
键名 | 键名 | 关键代码 |
KEYCODE_HOME | 按键Home | 3 |
KEYCODE_MENU | 菜单键 | 82 |
KEYCODE_BACK | 返回键 | 4 |
KEYCODE_VOLUME_UP | 音量增加键 | 24 |
KEYCODE_VOLUME_DOWN | 音量减少键 | 25 |
按键对应的编码,可以搜索关键字 “android keycode”
例如:https://blog.csdn.net/feizhixuan46789/article/details/16801429
19、打开和关闭手机通知栏
应用场景:想通过通知栏来判断手机是否收到消息,一定要先操作手机的通知栏
方法:
# 打开手机通知栏
driver.open_notifications()
# 关闭手机通知栏
driver.press_keycode(4) # 直接返回就能关闭,没有专门的方法关闭