Python+Appium+Pytest 自动化测试教程
- [1、App 自动化应用控制](#1、App 自动化应用控制)
- 2、常见控件定位方法
-
- 2.1、控件定位方式
- [2.2、控件查找方法 find_elements & find_element](#2.2、控件查找方法 find_elements & find_element)
- 2.3、常用定位方式
- [3、 强制等待与隐式等待](#3、 强制等待与隐式等待)
1、App 自动化应用控制
1.1、启动
启动应用主要有两种方式:
- 正常启动应用。
- 在脚本中启动其他应用
正常启动应用:创建一个 WebDriver 实例,用于与 Appium 服务器建立连接,并传递所需的启动配置(Desired Capabilities)。主要需要以下两个参数:
-
url:指定 Appium 服务器的 URL 地址。这通常是 Appium
服务器的主机名和端口号,例如http://localhost:4723。
-
capability:一个字典对象,包含了启动应用程序时的配置信息。这些配置信息可以包括设备名称、平台版本、应用程序包名、活动名称、自动化测试引擎、设备连接信息等。
通过将 URL 和 Capabilities 传递给启动方法,Appium 客户端库将与 Appium 服务器建立连接,并使用提供的配置信息启动相应的会话。这将创建一个可以用于与应用程序进行交互的 WebDriver 实例。
在脚本中启动其他应用:可以在设备上激活给定的应用程序,传入的 app_id 为指定应用的包名。
python
# 启动应用:
appium_server_url = 'http://localhost:4723'
driver = webdriver.Remote(appium_server_url,options=UiAutomator2Options().load_capabilities(caps))
# 在脚本中启动其他应用:
driver.activate_app("com.xx.xx")
1.2、关闭
- 关闭指定 app:关闭当前操作的 app,不会关闭驱动对象
- 关闭驱动对象:关闭当前所有的关联的 app,并关闭驱动对象
python
# 关闭指定 app
driver.terminate_app("com.xx.xx")
# 关闭当前所有的关联的 app,并关闭驱动对象
driver.quit()
2、常见控件定位方法
2.1、控件定位方式
在 Appium 中,控件定位是指通过一些标志性的特征来定位应用界面中的元素,以便进行自动化测试操作。Appium 支持多种控件定位方式,以下是一些常见的方法。
定位策略 | 描述 |
---|---|
Accessibility ID | 识别一个唯一的 UI 元素,对于 XCUITest 引擎,它对应的的属性名是 accessibility-id,对于 Android 系统的页面元素,对应的属性名是 content-desc |
Class name | 对于 iOS 系统,它的 class 属性对应的属性值会以XCUIElementType开头,对于 Android 系统,它对应的是 UIAutomator2 的 class 属性(e.g.: android.widget.TextView) |
ID | 原生元素的标识符,Android 系统对应的属性名为resource-id,iOS 为name |
Name | 元素的名称 |
XPath | 使用 XPath 表达式查找页面所对应的 xml 的路径 |
2.2、控件查找方法 find_elements & find_element
- 返回单个元素 WebElement
- 返回元素列表 [WebElement, WebElement, WebElement...]
python
# 返回单个元素 WebElement
driver.find_element(AppiumBy.xxx, "xxx属性值")
# 返回元素列表 [WebElement, WebElement, WebElement...]
driver.find_elements(AppiumBy.xxx, "xxx属性值")
# find_elements & find_element 一个有s
2.3、常用定位方式
ID 定位
- 通过身份标识 id 查找元素
- 写法:find_element(AppiumBy.ID, "ID属性值")
ACCESSIBILITY_ID 定位
- 通过 accessibility id 查找元素
- 写法:find_element(AppiumBy.ACCESSIBILITY_ID, "ACCESSIBILITY_ID属性值")
XPath 定位
- 单属性定位://*[@属性名='属性值']
- 多属性定位://*[@属性名='属性值' and @属性名='属性值' ]
表达式 | 描述 |
---|---|
/ | |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置(取子孙节点)。 |
. | 选取当前节点。 |
... | 选取当前节点的父节点。 |
@ | 选取属性。 |
演示代码
python
from appium import webdriver
# 设置 Desired Capabilities
desired_caps = {
'platformName': 'Android',
'deviceName': 'My Device',
'appPackage': 'com.example.app',
'appActivity': '.MainActivity'
}
# 初始化 WebDriver
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
# 通过 Accessibility ID 定位
element = driver.find_element_by_accessibility_id('someAccessibilityID')
# 通过 Class Name 定位
element = driver.find_element_by_class_name('android.widget.TextView')
# 通过 ID 定位
element = driver.find_element_by_id('someID')
# 通过 Name 定位
element = driver.find_element_by_name('someName')
# 通过 XPath 定位
element = driver.find_element_by_xpath('//android.widget.TextView[@text="Example Text"]')
# 与元素交互,例如点击
element.click()
# 关闭 WebDriver
driver.quit()
3、 强制等待与隐式等待
3.1、强制等待
解决方案:在报错的元素操作之前添加等待。
原理:线程休眠一定时间。
python
# 导入包
import time
time.sleep(3) # 等待3秒
3.2、隐式等待
问题:难以确定元素加载的具体等待时间。
解决方案:针对于寻找元素的这个动作,使用隐式等待添加配置。原理:隐式等待是一种全局的等待方式,设置一个等待时间,轮询查找(默认 0.5 秒)元素是否出现,如果没出现就抛出异常。
python
#设置一个等待时间,轮询查找(默认0.5秒)元素是否出现,如果没出现就抛出异常
driver.implicitly_wait(3) # 等待3秒
隐式等待无法解决的问题
元素可以找到,使用点击等操作,出现报错。
原因:
- 页面元素加载是异步加载过程,通常 xml 会先加载完成,相应的元素属性后加载。
- 元素存在与否是由 xml 决定,元素的交互是由属性决定。
- 隐式等待只关注元素能不能找到,不关注元素能否点击或者进行其他的交互。
解决方案:使用显式等待。
3.3、显式等待
示例: WebDriverWait(driver实例, 最长等待时间, 轮询时间).until(结束条件)。
原理:在最长等待时间内,轮询,是否满足结束条件。
python
# 导入需要的包
import time
from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
class TestWait:
def setup_class(self):
'''
完成 capability 设置
初始化 driver
:return:
'''
# 设置 cpability
caps = {
# 设置 app 安装的平台(Android,iOS)
"platformName": "Android",
# 设置 appium 驱动
"appium:automationName": "uiautomator2",
# 设置设备名称
"appium:deviceName": "遥遥领先",
# 设置被测 app 的包名
"appium:appPackage": "com.android.system",
# 设置被测 app 启动页面的 Activity
"appium:appActivity": ".ApiDxxsx"
}
# 初始化 driver
self.driver = webdriver.Remote(
"http://127.0.0.1:4723",
options=UiAutomator2Options().load_capabilities(caps)
)
# 设置全局隐式等待
self.driver.implicitly_wait(5)
def teardown_class(self):
'''
关闭 driver
:return:
'''
self.driver.quit()
def test_wait(self):
'''
点击 OS 按钮后等待 3 秒
显示等待 Morse Code 元素可点击
输入框输入内容后等待 2 秒
点击返回按钮后等待 2 秒
:return:
'''
# 测试步骤
# 找到 OS 元素
el1 = self.driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value="OS")
# 点击 OS 元素
el1.click()
# 等待 3 秒
time.sleep(3)
# 显示等待 Morse Code 元素可点击
el2 = WebDriverWait(self.driver, 10).until(
expected_conditions.element_to_be_clickable(
(AppiumBy.ACCESSIBILITY_ID, "Morse Code")
)
)
# 找到 Morse Code 元素
el2.click()
# 等待 3 秒
time.sleep(3)
# 找到输入框元素
el3 = self.driver.find_element(AppiumBy.ID, "io.appium.android.apis:id/text")
# 在输入框中输入内容
el3.send_keys("ceshiren.com")
# 等待 2 秒
time.sleep(2)
# 点击返回按钮
self.driver.back()
# 等待 2 秒
time.sleep(2)
# 点击返回按钮
self.driver.back()
# 断言:判断首页中第一个元素的文本内容是 Access'ibility
result = self.driver.find_element(
AppiumBy.XPATH,
"//*[@resource-id='android:id/text1'][1]"
)
print(result.text)
assert result.text == "Access'ibility"