Appium+Python 实现移动应用自动化测试:从基础到实战
在移动应用开发中,自动化测试是保障产品质量的关键环节。Appium 作为一款跨平台的移动自动化工具,支持 iOS 和 Android,且能与 Python 等主流语言结合,成为很多开发者的首选。本文将基于实际项目代码,带你掌握 Appium+Python 的核心用法,实现从应用登录到功能循环测试的全流程自动化。
一、环境准备:搭建 Appium 自动化基础
在开始编写代码前,需要先搭建好运行环境,确保设备、工具和依赖都配置到位。
1. 核心工具与依赖安装
- Appium Server:用于管理自动化会话的服务端,可通过Appium 官网下载桌面版,或通过 npm 安装命令行版(npm install -g appium)。
- Android SDK:提供 ADB(Android 调试桥)等工具,需配置ANDROID_HOME环境变量,并将platform-tools目录加入 Path(确保adb命令可全局执行)。
- Python 客户端:安装 Appium Python 库,执行pip install Appium-Python-Client。
- 测试设备:一台开启 “USB 调试” 的 Android 真机(或模拟器),确保通过 USB 连接电脑后,adb devices命令能识别到设备。
如果不会安装的话可以参考我之前写的博客Appium下载安装配置保姆教程(图文详解)
2. 设备连接检查
在编写代码前,先用 ADB 命令确认设备已正常连接:
# 查看已连接的设备
adb devices
若输出类似1234567890ABCDEF device,说明设备连接成功(device状态表示可用)。
二、核心操作:Appium+Python 基础用法
Appium 的核心是通过客户端代码与 Appium Server 通信,实现对设备的控制。以下是最常用的基础操作,也是自动化脚本的骨架。
1. 初始化 Appium 会话
要控制设备,需先创建一个 Appium 会话(Session),通过配置 “期望能力”(Desired Capabilities)指定设备和应用信息。
from appium import webdriver
from appium.options.android import UiAutomator2Options# 配置期望能力(Desired Capabilities)
CAPABILITIES = {"platformName": "Android", # 平台(Android/iOS)"deviceName": "1234567890ABCDEF", # 设备序列号(通过adb devices获取)"appPackage": "com.jlfc.tml", # 应用包名"appActivity": ".MainActivity", # 启动Activity"automationName": "UiAutomator2", # 自动化引擎(Android推荐UiAutomator2)"noReset": True # 不重置应用数据(避免每次启动都清除缓存)
}# 初始化会话:连接Appium Server(默认地址http://localhost:4723/wd/hub)
options = UiAutomator2Options().load_capabilities(CAPABILITIES)
driver = webdriver.Remote("http://localhost:4723/wd/hub", options=options)
- platformName:指定测试平台(必须正确,否则无法连接)。
- appPackage和appActivity:确定要启动的应用及入口(可通过adb命令获取,前文已讲)。
adb shell dumpsys window | findstr "mCurrentFocus"
- automationName:指定自动化引擎,Android 推荐UiAutomator2(支持最新系统版本)。
2. 元素定位与操作
自动化的核心是 “找到元素并操作”。Appium 支持多种定位方式,常用的有ID、XPath和文本,以下是实战中最常用的两种:
(1)通过 ID 定位(最稳定)
元素的resource-id是开发时设置的唯一标识,优先用 ID 定位:
from appium.webdriver.common.appiumby import AppiumBy# 定位“管理员用户”按钮(ID为com.jlfc.tml:id/txt_user_admin)
admin_btn = driver.find_element(by=AppiumBy.ID, value="com.jlfc.tml:id/txt_user_admin")
admin_btn.click() # 点击按钮
(2)通过 XPath 定位(最灵活)
当元素无唯一 ID 时,可用 XPath 结合文本、属性等定位:
# 定位文本为“固件版本升级”的选项
upgrade_item = driver.find_element(by=AppiumBy.XPATH, value="//android.widget.TextView[@text='固件版本升级']"
)
upgrade_item.click()# 定位包含“登录”文本的按钮
login_btn = driver.find_element(by=AppiumBy.XPATH, value="//android.widget.Button[contains(@text, '登录')]"
)
3. 等待机制:避免 “元素未加载” 问题
应用界面加载需要时间,直接定位元素可能导致 “找不到元素” 错误。需使用显式等待,等待元素处于可操作状态后再执行操作:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutExceptiondef wait_element_clickable(driver, locator, timeout=10):"""等待元素可点击"""try:# 等待timeout秒,直到元素可点击return WebDriverWait(driver, timeout).until(EC.element_to_be_clickable(locator))except TimeoutException:raise Exception(f"超时未找到可点击元素:{locator}")# 用法:等待“登录按钮”可点击后点击
login_btn_locator = (AppiumBy.ID, "com.jlfc.tml:id/btn_login")
wait_element_clickable(driver, login_btn_locator).click()
- 显式等待比time.sleep()更高效(无需固定等待时间,元素加载完成后立即执行)。
- 超时时间根据页面复杂度设置(一般 5-15 秒)。
4. 常用交互操作
除了点击,Appium 还支持输入文本、滑动、返回主页等常用操作:
# 输入文本(如账号密码)
username_input = driver.find_element(by=AppiumBy.ID, value="com.jlfc.tml:id/edit_username")
username_input.send_keys("yy_after") # 输入账号# 滑动屏幕(上滑,参数:起始x、起始y、结束x、结束y、持续时间)
def swipe_up(driver):size = driver.get_window_size() # 获取屏幕尺寸driver.swipe(size["width"] * 0.5, # 起始x(屏幕中间)size["height"] * 0.8, # 起始y(屏幕下方)size["width"] * 0.5, # 结束x(屏幕中间)size["height"] * 0.2, # 结束y(屏幕上方)500 # 滑动持续时间(毫秒))# 返回手机主页(按Home键,keycode=3)
driver.press_keycode(3)
5. 关闭会话
测试结束后,需关闭会话释放资源:
driver.quit() # 关闭当前会话,释放设备
三、实战案例:实现应用登录与循环测试
基于以上基础,我们可以组合出完整的自动化场景。以下面应用为例,实现 “管理员登录” 和 “固件升级循环测试” 两个核心流程。
1. 管理员登录流程
登录是大多数应用的基础操作,需处理 “应用已打开” 和 “需重新启动应用” 两种场景:
def login_admin(driver):# 检查是否已显示“管理员用户”按钮admin_btn_locator = (AppiumBy.ID, "com.jlfc.tml:id/txt_user_admin")try:# 若已显示,直接点击wait_element_clickable(driver, admin_btn_locator, timeout=2).click()print("已点击管理员用户按钮")except TimeoutException:# 若未显示,返回主页并重启应用driver.press_keycode(3) # 返回主页time.sleep(2)# 滑动查找应用图标并点击(最多滑3次)max_scrolls = 3for i in range(max_scrolls):try:# 定位应用图标(文本为“唐米力”)app_icon = wait_element_clickable(driver, (AppiumBy.XPATH, '//*[@text="唐米力"]'), timeout=5)app_icon.click()print("已启动应用")time.sleep(5) # 等待应用启动breakexcept TimeoutException:if i == max_scrolls - 1:raise Exception("未找到应用图标")swipe_up(driver) # 上滑屏幕print(f"已上滑{i+1}次")# 启动后点击管理员按钮wait_element_clickable(driver, admin_btn_locator).click()# 输入账号密码并登录wait_element_clickable(driver, (AppiumBy.ID, "com.jlfc.tml:id/edit_username")).send_keys("yy")wait_element_clickable(driver, (AppiumBy.ID, "com.jlfc.tml:id/edit_password")).send_keys("123456")wait_element_clickable(driver, (AppiumBy.ID, "com.jlfc.tml:id/btn_login")).click()# 验证登录成功(检查“自动炒菜”文本是否显示)wait_element_clickable(driver, (AppiumBy.XPATH, "//*[@text='自动炒菜']"), timeout=15)print("管理员登录成功")
2. 固件升级循环测试
很多场景需要重复执行某一操作(如多次测试固件升级),可通过循环实现,并加入异常处理确保稳定性:
def run_firmware_upgrade(driver, run_count=10):"""循环执行固件升级测试"""stop_flag = False # 用于标记是否停止后续测试for i in range(run_count):if stop_flag:print("检测到错误,停止后续升级测试")breaktry:print(f"\n===== 第{i+1}次固件升级 =====")# 检查会话是否有效(避免设备断开)try:driver.current_activity # 获取当前Activity,验证会话except Exception:raise Exception("会话已失效,需重新初始化")# 进入设置页面wait_element_clickable(driver, (AppiumBy.ID, "com.jlfc.tml:id/img_settings")).click()# 点击“固件版本升级”wait_element_clickable(driver, (AppiumBy.XPATH, "//*[@text='固件版本升级' and @resource-id='android:id/title']")).click()# 点击确认升级wait_element_clickable(driver, (AppiumBy.ID, "com.jlfc.tml:id/btn_confirm")).click()# 等待升级完成(最长360秒)wait_element_clickable(driver, (AppiumBy.ID, "com.jlfc.tml:id/btn_login"), # 升级后回到登录页timeout=360)print(f"第{i+1}次升级完成,已返回登录页")# 点击登录按钮,准备下一次测试wait_element_clickable(driver, (AppiumBy.ID, "com.jlfc.tml:id/btn_login")).click()except Exception as e:# 捕获异常,标记停止后续测试print(f"第{i+1}次升级失败:{str(e)}")stop_flag = True# 可在此处添加截图功能(见下文)
3. 异常处理与截图
测试出错时,截图能帮助定位问题,可封装一个截图函数:
import os
from datetime import datetimedef take_screenshot(driver, name):"""保存截图到本地"""# 创建截图目录screenshot_dir = "screenshots"if not os.path.exists(screenshot_dir):os.makedirs(screenshot_dir)# 截图文件名(包含时间戳)timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")filename = f"{screenshot_dir}/{name}_{timestamp}.png"# 保存截图driver.save_screenshot(filename)print(f"截图已保存:{filename}")return filename# 用法:在异常时调用
try:# 测试步骤...
except Exception as e:take_screenshot(driver, "upgrade_failure")raise e
四、自动化测试最佳实践
- 优先使用稳定的定位方式:ID > XPath(属性组合) > 文本(文本可能随语言变化)。
- 合理设置等待时间:避免用time.sleep(),多用显式等待;根据页面加载速度调整超时时间。
- 处理会话失效:设备断开、应用崩溃可能导致会话失效,需在关键步骤前检查会话状态(如driver.current_activity)。
- 循环测试加中断机制:重复测试时,一旦出现致命错误,立即停止后续执行(如上文的stop_flag),避免无效执行。
- 及时清理资源:测试结束后务必调用driver.quit(),释放设备连接,避免影响其他测试。
到这里我的分享就结束了,欢迎到评论区探讨交流!!
💖如果觉得有用的话还请点个赞吧 💖