杰瑞科技汇

Appium Python API 怎么用?

目录

  1. 环境准备

    • 安装 Python
    • 安装 Appium Server
    • 安装 Python 客户端库
    • 安装手机驱动/模拟器
    • 验证环境
  2. 核心概念与第一个脚本

    • Desired Capabilities (必需能力)
    • Driver 对象
    • 第一个自动化脚本:启动并关闭 App
  3. 核心 API 分类详解

    • 元素定位
    • 元素交互
    • 获取信息
    • 等待
    • 手势操作
    • 应用控制
    • Toast 消息获取
    • 多点触控
  4. 最佳实践与设计模式

    • Page Object Model (POM) 页面对象模型
    • 使用 pytest 框架
    • 日志记录
    • 配置管理
  5. 进阶主题

    • 处 Webview (Hybrid App)
    • 处理权限弹窗
    • 参数化测试
    • 在 CI/CD 中使用 Appium

环境准备

1 安装 Python

确保你的系统已安装 Python (推荐 3.7+ 版本),打开终端或命令行,输入:

python --version
# 或
python3 --version

如果没有安装,请从 Python 官网 下载并安装。

2 安装 Appium Server

Appium Server 是一个中间件,负责将你的 Python 脚本命令翻译成在移动设备上可以执行的操作。

  • 最简单方式 (推荐新手): 下载 Appium Desktop 并安装它,图形化界面方便启动和管理 Server。
  • 命令行方式: 使用 Node.js 的包管理器 npm 安装。
    npm install -g appium

    安装后,可以通过 appium 命令启动 Server。

3 安装 Python 客户端库

这是连接你的 Python 脚本和 Appium Server 的桥梁。

pip install Appium-Python-Client

这个库包含了所有与 Appium 交互所需的类和方法。

4 安装手机驱动/模拟器

你需要一个目标设备来运行你的测试脚本。

  • 真机: 确保开启了“开发者模式”和“USB 调试”。
  • Android 模拟器: Android Studio 自带的模拟器或 Genymotion。
  • iOS 模拟器: Xcode 自带的模拟器 (仅限 macOS)。

5 验证环境

一个简单的验证步骤:

  1. 启动 Appium Desktop。
  2. 启动你的 Android 模拟器或连接真机。
  3. 在 Appium Desktop 的 "Desired Capabilities" 配置中填入基本信息(见下一节),然后点击 "Start Session"。

    如果能成功启动一个会话并看到一个黑色的、可交互的屏幕,说明你的环境基本没问题。


核心概念与第一个脚本

1 Desired Capabilities (必需能力)

Desired Capabilities 是一个 JSON 对象,用来告诉 Appium Server 你想启动什么样的会话,比如在哪个设备上、启动哪个 App 等。

常见 Android Capabilities:

desired_caps = {
    "platformName": "Android",  # 平台名称
    "platformVersion": "11",    # Android 系统版本
    "deviceName": "Pixel_4_API_30", # 设备名称 (可自定义)
    "app": "/path/to/your/app.apk", # App 的绝对路径
    "automationName": "UiAutomator2", # 自动化引擎 (推荐)
    "noReset": False,           # 会话结束后是否不重置 App 状态
    "fullReset": False,         # 会话开始前是否完全重置 App (卸载重装)
    "unicodeKeyboard": True,    # 使用 Unicode 字符集的键盘
    "resetKeyboard": True,      # 测试结束后重置键盘状态
    "newCommandTimeout": 600    # Appium Server 等待新命令的超时时间(秒)
}

2 Driver 对象

Driver 对象是你与 Appium 交互的入口,所有对设备的操作,如点击、滑动、查找元素等,都是通过 driver 对象来完成的。

3 第一个自动化脚本

这个脚本的功能是:启动计算器 App,点击 5 + 3 =,然后获取结果并打印。

# 导入必要的库
from appium import webdriver
import time
# 定义 Desired Capabilities
desired_caps = {
    "platformName": "Android",
    "platformVersion": "11",
    "deviceName": "Pixel_4_API_30",
    "app": "/path/to/your/calculator.apk", # 替换成你的计算器APK路径
    "automationName": "UiAutomator2",
    "noReset": True,
    "fullReset": False
}
# 初始化 driver
try:
    driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
    print("成功启动 App 会话!")
    # --- 开始自动化操作 ---
    # 1. 定位数字5并点击
    driver.find_element_by_id('com.android.calculator2:id/digit_5').click()
    # 2. 定位加号并点击
    driver.find_element_by_id('com.android.calculator2:id/op_add').click()
    # 3. 定位数字3并点击
    driver.find_element_by_id('com.android.calculator2:id/digit_3').click()
    # 4. 定位等号并点击
    driver.find_element_by_id('com.android.calculator2/id/eq').click() # 注意:这个ID可能需要你用UI Automator Viewer确认
    # 5. 获取结果文本
    result_element = driver.find_element_by_id('com.android.calculator2:id/result')
    result_text = result_element.text
    print(f"计算结果是: {result_text}")
    # 等待3秒,方便观察
    time.sleep(3)
except Exception as e:
    print(f"发生错误: {e}")
finally:
    # 6. 关闭会话
    if 'driver' in locals():
        driver.quit()
        print("App 会话已关闭。")

如何获取元素 ID/Class Name/...? 强烈推荐使用 Appium Desktop 自带的 Inspector 功能:

  1. 在 Appium Desktop 中填好 Desired Capabilities
  2. 点击 "Start Session" 启动会话。
  3. 在打开的设备界面中,点击右上角的 "放大镜" 图标。
  4. 现在你可以点击 App 上的任何元素,Inspector 会实时显示该元素的 resource-id, text, content-desc, class name 等信息,这些都是你定位元素的关键。

核心 API 分类详解

1 元素定位

Appium Python Client 支持多种定位策略,优先推荐 IDAccessibility ID

定位方法 Python 语法 描述 优先级
ID driver.find_element_by_id('id_value') 通过 resource-id 定位,最快、最稳定。 ⭐⭐⭐⭐⭐
Accessibility ID driver.find_element_by_accessibility_id('content-desc') 通过 content-desclabel 定位,语义化强,稳定。 ⭐⭐⭐⭐⭐
XPath driver.find_element_by_xpath('//android.widget.TextView[@text="Settings"]') 强大的路径语言,可定位任何元素,但性能稍差。 ⭐⭐⭐
Class Name driver.find_element_by_class_name('android.widget.TextView') 通过 UI 组件的类名定位,可能不唯一。 ⭐⭐
Android UI Automator driver.find_element_by_android_uiautomator('new UiSelector().text("OK")') 使用 Android UIAutomator2 的语法,非常灵活。 ⭐⭐⭐⭐

注意: find_element_by_* 是查找单个元素,如果找不到会抛出 NoSuchElementExceptionfind_elements_by_* (注意是 elements 复数) 是查找所有匹配的元素,返回一个列表,如果找不到则返回空列表。

2 元素交互

找到元素后,你可以对它进行操作。

方法 描述
.click() 点击元素
.send_keys("文本内容") 向输入框输入文本
.clear() 清空输入框内容
.submit() 提交表单 (类似点击一个提交按钮)

示例:

search_box = driver.find_element_by_id('com.example.app:id/search_input')
search_box.clear()
search_box.send_keys("Appium Python")
search_box.submit()

3 获取信息

你可以获取元素的属性或状态信息。

属性/方法 描述
.text 获取元素的可见文本
.get_attribute('attribute_name') 获取元素的任意属性,如 resource-id, content-desc, enabled, displayed
.size 获取元素的尺寸 (字典格式: {'width': 100, 'height': 50})
.location 获取元素在屏幕上的坐标 (字典格式: {'x': 100, 'y': 200})
.is_enabled() 判断元素是否可点击
.is_displayed() 判断元素是否可见

示例:

element = driver.find_element_by_id('some_id')
print(f"元素文本: {element.text}")
print(f"元素ID: {element.get_attribute('resource-id')}")
print(f"元素是否可见: {element.is_displayed()}")

4 等待

显式等待 是自动化测试的基石,它会让脚本在指定时间内等待某个条件满足(如元素出现)后再继续执行,而不是死等或直接报错。

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 等待最多10秒,直到ID为 'my_element' 的元素可见
try:
    element = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located(('id', 'my_element'))
    )
    print("元素已成功加载!")
    element.click()
except TimeoutException:
    print("等待10秒后,元素仍未出现!")
# 常用的 Expected Conditions:
# EC.presence_of_element_located  # 元素存在于DOM中,不一定可见
# EC.visibility_of_element_located # 元素可见
# EC.element_to_be_clickable       # 元素可点击contains("标题")         # 页面标题包含特定文本

5 手势操作

手势操作需要导入 TouchAction 类。

from appium.webdriver.common.touch_action import TouchAction
from appium.webdriver.common.multi_action import MultiAction
# --- 单点触摸 ---
action = TouchAction(driver)
# 点击坐标 (x, y)
action.tap(x=100, y=200).perform()
# 从元素A滑动到元素B
element_a = driver.find_element_by_id('a')
element_b = driver.find_element_by_id('b')
action.press(element_a).move_to(element_b).release().perform()
# 长按元素
action.long_press(element_a).release().perform()
# --- 多点触控 ---
# 模拟双指缩放
multi_action = MultiAction(driver)
action1 = TouchAction(driver).press(x=100, y=100).wait(1000).move_to(x=200, y=200).release()
action2 = TouchAction(driver).press(x=300, y=300).wait(1000).move_to(x=200, y=200).release()
multi_action.add(action1, action2).perform()

6 应用控制

方法 描述
driver.activate_app(package_name) 激活/启动 App (如果后台运行则拉到前台)
driver.terminate_app(package_name) 关闭 App (不卸载)
driver.remove_app(package_name) 卸载 App
driver.background_app(seconds) 将 App 切换到后台指定秒数
driver.is_app_installed(package_name) 检查 App 是否已安装
driver.install_app('/path/to/app.apk') 安装 App

7 Toast 消息获取

Android 的 Toast 消息默认无法通过标准定位方式获取,需要借助 autojs 或使用 UIAutomator 的特定方法。

方法一 (UIAutomator - 推荐且简单):

# Toast 消息通常是一个类名为 'android.widget.Toast' 的文本
toast_element = driver.find_element_by_class_name('android.widget.Toast')
toast_text = toast_element.text
print(f"获取到 Toast: {toast_text}")

方法二 (使用 AutoJS - 更稳定): 这种方法需要在 Appium 启动参数中启用 autojs 服务,并安装 AutoJS App。

# 在 desired_caps 中添加
"autojs": "true"
# 然后可以直接执行 AutoJS 脚本来获取
js_script = "toast.getRecentToast()"
toast_text = driver.execute_script(js_script)

8 多点触控

请参考 5 手势操作 中的 MultiAction 示例。


最佳实践与设计模式

1 Page Object Model (POM) 页面对象模型

POM 是一种设计模式,它将页面元素和操作页面的方法封装到一个类中,使测试代码更清晰、可维护。

结构:

tests/
|-- pages/
|   |-- login_page.py      # 登录页面对象
|   |-- home_page.py       # 首页面对象
|-- test_cases/
|   |-- test_login.py      # 登录测试用例
|-- conftest.py            # pytest 配置
|-- config.py              # 配置文件

示例代码:

pages/login_page.py

class LoginPage:
    def __init__(self, driver):
        self.driver = driver
        # 定位器
        self.username_field = ('id', 'com.example.app:id/username')
        self.password_field = ('id', 'com.example.app:id/password')
        self.login_button = ('id', 'com.example.app:id/login_btn')
        self.error_message = ('id', 'com.example.app:id/error_msg')
    def login(self, username, password):
        """登录操作"""
        self.driver.find_element(*self.username_field).send_keys(username)
        self.driver.find_element(*self.password_field).send_keys(password)
        self.driver.find_element(*self.login_button).click()
    def get_error_text(self):
        """获取错误信息"""
        return self.driver.find_element(*self.error_message).text

test_cases/test_login.py

import pytest
from appium import webdriver
from pages.login_page import LoginPage
@pytest.fixture
def driver():
    # 初始化 driver
    desired_caps = {...} # 你的 caps
    driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
    yield driver
    driver.quit()
def test_login_with_invalid_credential(driver):
    login_page = LoginPage(driver)
    login_page.login("wrong_user", "wrong_pass")
    error_text = login_page.get_error_text()
    assert "用户名或密码错误" in error_text

2 使用 pytest 框架

pytest 是一个强大的 Python 测试框架,支持简单的测试发现、丰富的插件(如 pytest-html 生成报告)和参数化。

安装:

pip install pytest pytest-html

运行测试并生成报告:

pytest test_cases/ --html=report.html

3 日志记录

使用 Python 内置的 logging 模块记录测试过程,便于调试和追踪。

import logging
# 配置日志
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def test_example(driver):
    logger.info("开始执行测试用例")
    try:
        element = driver.find_element_by_id('my_element')
        logger.info("成功找到元素")
        element.click()
        logger.info("成功点击元素")
    except Exception as e:
        logger.error(f"测试失败: {e}", exc_info=True)
        raise

4 配置管理

Desired Capabilities、服务器地址等配置信息与测试代码分离。

config.py

APPIUM_SERVER_URL = 'http://localhost:4723/wd/hub'
ANDROID_CAPS = {
    "platformName": "Android",
    "platformVersion": "11",
    "deviceName": "Pixel_4_API_30",
    "app": "/path/to/your/app.apk",
    "automationName": "UiAutomator2",
    "noReset": True,
}

在测试文件中引用:

from appium import webdriver
import config
driver = webdriver.Remote(config.APPIUM_SERVER_URL, config.ANDROID_CAPS)

进阶主题

1 处理 Webview (Hybrid App)

对于混合应用(原生 + Web),需要切换到 Web 上下文进行操作。

# 1. 获取所有可用的上下文
contexts = driver.contexts
print(f"所有上下文: {contexts}")
# 2. 切换到 NATIVE_APP (原生) 或 WEBVIEW (Web)
# WEBVIEW 的名字是 'WEBVIEW_com.example.app'
webview_context = None
for context in contexts:
    if 'WEBVIEW' in context:
        webview_context = context
        break
if webview_context:
    driver.switch_to.context(webview_context)
    print("已切换到 Webview 上下文")
    # 现在可以使用 Selenium 的 API 操作 Web 元素
    driver.find_element_by_tag_name('input').send_keys('Hello Web')
    # 操作完成后,切回原生上下文
    driver.switch_to.context('NATIVE_APP')
    print("已切回原生上下文")

2 处理权限弹窗

不同 Android 版本的权限弹窗处理方式不同。

  • Android 6.0-10: 通常可以直接通过点击 "允许" 或 "拒绝" 按钮来处理。
  • Android 11+: 权限弹窗可能由系统级控制,Appium 无法直接操作,需要在测试前手动开启权限,或在 desired_caps 中使用 autoGrantPermissions 参数。

示例 (处理旧版弹窗):

# 尝试定位并点击 "允许" 按钮
# 注意:这些定位器需要根据实际情况调整
try:
    allow_button = driver.find_element_by_id('com.android.packageinstaller:id/permission_allow_button')
    allow_button.click()
    print("点击了允许按钮")
except:
    print("未找到权限弹窗或已处理")

3 参数化测试

使用 pytest@pytest.mark.parametrize 可以轻松地对同一组操作使用不同的输入数据。

import pytest
data = [
    ("user1", "pass1", "Success"),
    ("user2", "pass2", "Success"),
    ("user3", "wrong", "Failed")
]
@pytest.mark.parametrize("username, password, expected_result", data)
def test_login(driver, username, password, expected_result):
    login_page = LoginPage(driver)
    login_page.login(username, password)
    if expected_result == "Success":
        assert "欢迎页" in driver.title
    else:
        assert "错误" in login_page.get_error_text()

4 在 CI/CD 中使用 Appium

在持续集成/持续部署 (如 Jenkins, GitLab CI) 环境中运行 Appium 测试,关键点在于:

  1. 环境隔离: 使用 Docker 容器来打包 Appium Server、测试依赖和被测 App,确保环境一致性。
  2. 设备管理: 使用云设备平台(如 BrowserStack, Sauce Labs)或本地设备农场管理工具(如 Appium Desktop 的 "Edit Configs -> Automatic Server")来提供测试设备。
  3. 无头模式: Appium Server 可以在无图形界面模式下运行。
  4. 报告集成:pytest-html 等生成的测试报告上传到 CI 系统中。

希望这份详尽的指南能帮助你快速上手 Appium Python 自动化测试!祝你编码愉快!

分享:
扫描分享到社交APP
上一篇
下一篇