【自动化测试】appium + python环境部署 + 代码应用

启动一个软件页面
# coding=gbk
# 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行
from appium import webdriver
import time
#创建一个字典
desired_caps = dict()
#系统种类 ios程序填'ios'
desired_caps['platformName'] = 'Android'
#系统版本 Android6.1
desired_caps['platformVersion'] = '6.0.1'
#设备名字 安卓随意
desired_caps['deviceName'] = 'MI NOTE 3'
#软件包名
desired_caps['appPackage'] = 'com.v2ray.ang'
#页面名
desired_caps['appActivity'] = '.ui.MainActivity'
#使用unicodeKeyboard的编码方式来发送字符串
desired_caps['unicodeKeyboard'] = True
#将键盘给隐藏起来
desired_caps['resetKeyboard'] = True
#不重置app
desired_caps['noReset'] = True
#生成 driver 对象并启动
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

获取app的包名和界面名
# 获取包名
driver.current_package
# 获取界面名
driver.current_activity
通过代码跳转到其他app
  • 通过driver对象调用 start_activity 的方法

    start_activity("包名", "界面名")
    
如果通过代码获取 app 的包名和界面名
  • 通过 driver 对象调用 current_package 属性
  • 通过 driver 对象调用 current_activity 属性
关闭 app 和驱动程序
# 关闭当前操作的app, 不会关闭驱动对象
driver.close_app
# 关闭驱动对象, 同时关闭所有关联的app
driver.quit()
安装和卸载以及是否安装 app
应用场景

一些应用市场的软件可能会有一个按钮, 如果某个程序已经安装则卸载, 如果没有安装则安装

# 安装app
# 参数:
# 	app_path: apk路径
driver.install_app(app_path)
# 卸载app
# 参数: 
# 	app_id: 应用程序包名
driver.remove(app_id)
# 判断app是否已经安装
# 参数:
# 	app_id: 应用程序包名
# 返回值: 
# 	布尔类型: True为安装, False为没有安装
driver.is_app_installed(app_id)
样例
# coding=gbk
from time import time
from appium import webdriver
import time

desired_caps = dict()

desired_caps['platformName'] = "Android"
desired_caps['platformVersion'] = '6.0.1'
desired_caps['deviceName'] = 'mushan'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'

driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
driver.close_app()
# 如果存在就卸载, 否则就安装
if driver.is_app_installed("com.v2ray.ang"):
    driver.remove_app("com.v2ray.ang")
    print("已卸载")
else:
    driver.install_app("C:/Users/lenovo/Downloads/Programs/v2rayNG.apk")
    print("已安装")

driver.quit()

元素定位操作 API

当我们选择一个控件, 可以在软件右下角查看控件相关信息, 通过这些内容来定位一个控件

应用场景

想要对按钮进行点击, 输入框进行输入, 首先要定位按钮和文本框, 定位元素是自动化操作必须要使用的方法, 只有获取元素之后, 才能对这个元素进行操作

方法名
# 通过ID定位一个元素
# 参数:
# 	id_val: 元素的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)
# xpath_value 格式
# 	“//*[@键='值']"
示例

模拟 v2rayNG, 节点订阅

通过 id 的形式, 定位左上角 " 三个杠 " 按钮并点击

点击订阅设置

点击右上角加号

定位备注输入框, 输入"木杉"

定位节点地址, python中获取节点并输入

返回起始界面

定位并点击"三个点"

点击更新订阅

代码:
# coding=gbk
from time import time
from appium import webdriver
import time

desired_caps = dict()

Data = "http://121.4.209.91:8888/down/b6OPLBLTdci9"

desired_caps['platformName'] = "Android"
desired_caps['platformVersion'] = '6.0.1'
desired_caps['deviceName'] = 'mushan'
desired_caps['appPackage'] = 'com.v2ray.ang'
desired_caps['appActivity'] = '.ui.MainActivity'

driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

# class定位菜单图标
Menu_button = driver.find_element_by_class_name("android.widget.ImageButton")
Menu_button.click()     # 单击按钮

# id 定位节点订阅按钮
Subscribe_button = driver.find_element_by_id("com.v2ray.ang:id/design_menu_item_text")
Subscribe_button.click()

# id 定位节点添加订阅按钮
Add_config = driver.find_element_by_id("com.v2ray.ang:id/add_config")
Add_config.click()

# id 定位备注输入框, 并输入"木杉乀"
driver.find_element_by_id("com.v2ray.ang:id/et_remarks").send_keys("MuShan")
# id 定位节点地址输入框, 并输入节点地址内容
driver.find_element_by_id("com.v2ray.ang:id/et_url").send_keys(Data)
# 单击确认
driver.find_element_by_id("com.v2ray.ang:id/save_config").click()
# 跳转到起始页
driver.start_activity("com.v2ray.ang", ".ui.MainActivity")

# 定位三个点并单击
driver.find_element_by_class_name("android.widget.ImageView").click()
# 更新订阅
driver.find_element_by_xpath("//*[@text='更新订阅']").click()

time.sleep(5)
driver.quit()

如何定位一组元素

与定位一个元素相似, 唯一的不同点就是将, find_element 变成了 find_elements

如果通过一组的方式定位, 获取到的不是第一个元素, 而是符合该特征的所有元素存放在一个集合里返回

定位元素注意点

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 方法, 传入一个没有的特征, 会报错: “没有找到”
  2. 如果使用 find_elements_by_xx 方法, 传入一个没有的特征, 不会报错, 会返回一个空列表

元素等待

应用场景

因为某些原因, 导致我们想要找的元素没有立刻出来, 此时如果直接定位可能会报错, 比如以下原因;

  1. 由于网络速度原因
  2. 服务器处理请求原因
  3. 电脑配置原因

此时不能让程序因为这些问题导致返回错误结果, 那么就需要使用元素等待

概念

WebDriver定位元素是如果未找到, 会在指定的时间内一直等待过程

元素等待分为两种

  1. 显式等待
  2. 隐式等待

隐式等待

应用场景

针对所有定位元素的超时时间为同一个值的时候

概念

等待元素加载指定时长, 如果在规定时间内找到就正常返回, 否则抛出NoSuchElemnetException异常

步骤

在获取 driver 对象后, 使用 driver 调用 implicitly_wait 方法即可

核心代码
from selenium.webdriver.support.wait import WebDriverWait

driver.implicitly_wait(5)			# 此方法使用后, 后面所有元素都会采用该等待
search_button = driver.find_element_by_xpath("//*[contains(@content-desc, '收起')]")
search_button.click()

显式等待

应用场景

针对所有定位元素的超时时间设置为不同的值的时候

概念

等待元素加载指定的时长, 超出时长抛出TimeoutException异常

步骤
  1. 导包
  2. 创建 WebDriverWait 对象
  3. 调用 WebDriverWait 对象 until 方法
核心代码
from selenium.webdriver.support.wait import WebDriverWait

wait = WebDriverWait(driver, 5, poll_frequency=1) # 默认0.5秒
back_button = wait.until(lambda x: x.find_element_by_id("com.v2ray.ang:id/fab"))
back_button.click
driver.quit()
创建wait对象: WebDriverWait的参数:
  1. driver对象
  2. 等待的时间(秒)
  3. 测试的频率(每间隔多长时间调用一次方法, 默认为0.5s)
与隐式等待的一个区别就是控制单个方法的等待
WebDriverWait(driver, 20, 3).until(lambda x : x.find_element_by_id("cpm.v2ray.ang:id/fab"))
将应用置于后台
应用场景

银行类 app 会进入后台一定时间后, 如果回到前台页面会重新输入密码, 如果需要自动化测试这种功能, 可以使用这个 api 进行测试

方法
# app放置到后台一定时间后再回到前台, 模拟热启动
# 参数:
# 	seconds: 后台停留多少秒
driver.background_app(seconds)
示例

打开 《设置》应用, 进入后台 5 秒, 再回到前台

# coding=gbk
from time import time
from appium import webdriver
import time

desired_caps = dict()

desired_caps['platformName'] = "Android"
desired_caps['platformVersion'] = '6.0.1'
desired_caps['deviceName'] = 'mushan'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'

driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

print("---- 准备进入后台 ----")
driver.background_app(5)
print("---- 回到前台 ----")

driver.quit()

元素操作API

点击元素

方法名:

element.click()

输入和清空输入框内容

方法名:

# 输入框输入value
element.send_keys(value)
# 清空输入框内容
element.clear()
注意:

默认输入中文是会出问题的, 需要在连接手机的参数中添加两行代码

desired_caps['unicodeKeyboard'] = True
desired_caps['resetKeyboard'] = True

获取元素文本内容

# 获取所有节点地址以及端口号
driver.implicitly_wait(3)
title = driver.find_elements_by_id("com.android.settings:id/title")
for i in title:
    print(i.text)

获取元素的位置和大小

# 获取element的位置
# 返回值:
# 	字典: x: x坐标, y: y坐标
element.location
# 获取element的大小
# 返回值:
# 	字典: width: 宽度, height: 高度
element.size

get——attribute(“属性名”)获取属性

content-desc属性获取

get_attribute(“name”) 获取content-desc属性,这里注意了,如果content-desc属性为空,那么获取的就是text属性,不为空获取的才是content-desc属性

备注:content-desc属性也可以这样获取:get_attribute(“contentDescription”)

id,calss,text属性获取
driver.find_element_by_id("com.baidu.yuedu:id/lefttitle").get_attribute("resourceId")
driver.find_element_by_id("com.baidu.yuedu:id/lefttitle").get_attribute("className")
driver.find_element_by_id("com.baidu.yuedu:id/lefttitle").get_attribute("text")
其它属性获取
driver.find_element_by_id("com.baidu.yuedu:id/lefttitle").get_attribute("checkable")
driver.find_element_by_id("com.baidu.yuedu:id/lefttitle").get_attribute("clickable")

滑动事件

swipe 滑动
driver.swipe(start_x坐标, start_y坐标, end_x坐标, end_y坐标, 持续时长)
# 时长参数含默认值
# 长度相同, 滑动时长越长, 惯性越小
# 长度相同, 滑动时长越短, 惯性越大

例:

driver.swipe(89, 865, 89, 371, 5000)
# 从坐标(89, 865) -> 坐标(89, 371) 滑动时长5000ms

scroll 滑动
driver.scroll(元素1, 元素2)
# 从元素1 滑动到 元素2
# 不能设置持续时长, 惯性很大

例:

save_button = driver.find_element_by_xpath("//*[@text='存储设备和 USB']")
more_button = driver.find_element_by_xpath("//*[@text='更多']")
driver.scroll(save_button, more_button)

drag_and_drop 拖拽
driver.drag_and_drop(元素1, 元素2)
# 从元素1 拖拽到 元素2
# 不能设置时长, 惯性极小
# 第二个元素替代第一个元素原本屏幕上的位置

例:

save_button = driver.find_element_by_xpath("//*[@text='存储设备和 USB']")
more_button = driver.find_element_by_xpath("//*[@text='更多']")
driver.drag_and_drop(save_button, more_button)

TouchAction类

from appium import webdriver
# 需要导入模块TouchAction
from appium.webdriver.common.touch_action import TouchAction

desired_caps = {
    "platformName": "Android",
    "platformVersion": "10",
    "deviceName": "PCT_AL10",
    "appPackage": "com.ss.android.article.news",
    "appActivity": ".activity.MainActivity",
    "automationName": "uiautomator2",
    "unicodeKeyboard": True,
    "resetKeyboard": True,
    "noReset": False,
}
# 启动app
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

# 构造TouchAction实例对象
action = TouchAction(driver)
按住点(x1, y1),等待1000ms,滑动至点(x2, y2),释放
action.press(x=x1, y=y1).wait(1000).move_to(x=x2, y=y2).release()
# 执行操作
action.perform()

tap点击:

# 点击元素的中心点
tap(element=ele1)
# 点击坐标(x1, y1)
tap(x=x1, y=y1)
# 以元素ele1左上角的x坐标向右移动x2单位,y坐标向下移动y2单位,在点(x+x2, y+y2)上点击
tap(element=ele1, x=x2, y=y2)

press短按:

# 按压元素
press(el=ele1)
# 按压坐标
press(x=x1, y=y1)
# 以元素ele1左上角的x坐标向右移动x2单位,y坐标向下移动y2单位,在点(x+x2, y+y2)上按压
press(el=ele1, x=x2, y=y2)

long_press长按

# 按压元素,默认1000ms
long_press(el=ele1)
# 按压坐标500ms
long_press(x=x1, y=y1, duration=500)
# 以元素ele1左上角的x坐标向右移动x2单位,y坐标向下移动y2单位,在点(x+x2, y+y2)上按压
long_press(el=ele1, x=x2, y=y2)

move_to移动至目标点

# 该方法需要与press()、long_press()结合使用

# 从另一个点移动至目标元素ele1
move_to(el=ele1)
# 从另一个点移动至点(x1, y1)
move_to(x=x1, y=y1)
# 从另一个点移动至点(x+x2, y+y2), (x, y)为元素ele1左上角的坐标
move_to(el=ele1, x=x2, y=y2)

wait等待

# 等待,如等待500ms
wait(500)

release释放

# 释放操作,与按压、长按结合使用
release()

perform()执行

# 将动作命令发送至服务器来执行该动作,如:
action = TouchAction(driver).press(x=x1, y=y1).move_to(x=x2, y=y2).release()
执行滑动操作
action.perform()

多点触控MultiAction类

  • add(self, touch_actions),参数touch_actions为触摸操作集合,将一个或多个触摸操作添加至当前的多点触控实例中
  • perform(self),执行多点触控操作

使用场景,如页面的放大、缩小等

from appium import webdriver
from appium.webdriver.common.touch_action import TouchAction
# 需要导入模块MultiAction
# from appium.webdriver.common.multi_action import MultiAction

desired_caps = {
    "platformName": "Android",
    "platformVersion": "10",
    "deviceName": "PCT_AL10",
    "appPackage": "com.ss.android.article.news",
    "appActivity": ".activity.MainActivity",
    "automationName": "uiautomator2",
    "unicodeKeyboard": True,
    "resetKeyboard": True,
    "noReset": False,
}
# 启动app
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps

# 创建两个触摸事件
action = TouchAction(driver)
action1 = action.press(ele1).move_to(ele2).release()
action2 = action.press(x=50, y=50).move_to(x=100, y=200).release()

# 创建MultiAction实例对象
multi_action = MultiAction(driver)
# 将触摸事件加入TouchAction对象
multi_action.add(action1, action2)

# 执行事件
multi_action.perform()

手机API操作

获取手机分辨率

  • 关键方法:
    • driver.get_window_size()
  • 返回值:
    • 字典
    • 两个key, 分别是 width 和 height
    • 宽和高的值是 int 类型的

如何截图

  • 关键方法:
    • driver.get_screenshot_as_file(“路径”)
  • 参数:
    • 文件的路径
    • 如果直接写文件名, 默认保存在项目目录下

获取和设置手机网络

手机网络三个状态:

  1. 流量
  2. wifi
  3. 飞行模式

在这里插入图片描述

由0, 1, 2, 4, 6 表示这三个状态

获取手机网络:

核心: driver.network_connection()

设置手机网络:

头文件: from appium.webdriver.connectiontype import ConnectionType

核心:driver.set_network_connection()

参数可以用数字, 也可以用对应宏

NO_CONNECTION = 0
AIRPLANE = 1
WIFI_ONLY = 2
DATA_ONLY = 4
ALL_NETWORK_ON = 6

发送键到设备

模拟"返回键" "home键"等操作

方法名
# 发送键到设备
# 参数:
# 	keycode: 发送给设备的关键码
# 	metastate: 关于被发送的关键代码的元信息, 一般为默认值
driver.press_keycode(keycode, metastate = None)

操作手机通知栏

方法名
# 打开手机通知栏
driver.open_notifications()

API中没有关闭通知栏的语法, 通过手指滑动等自动化操作进行关闭

{
  "platformName": "Android",
  "platformVersion": "6.0.1",
  "deviceName": "MI NOTE 3",
  "appPackage": "com.tencent.wework",
  "appActivity": ".launch.WwMainActivity",
  "unicodeKeyboard": true,
  "resetKeyboard": true,
  "noReset": true
}
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MuShan-bit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值