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 命令确认设备已正常连接:

bash 复制代码
# 查看已连接的设备
adb devices

若输出类似1234567890ABCDEF device,说明设备连接成功(device状态表示可用)。

二、核心操作:Appium+Python 基础用法

Appium 的核心是通过客户端代码与 Appium Server 通信,实现对设备的控制。以下是最常用的基础操作,也是自动化脚本的骨架。

1. 初始化 Appium 会话

要控制设备,需先创建一个 Appium 会话(Session),通过配置 "期望能力"(Desired Capabilities)指定设备和应用信息。

python 复制代码
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命令获取,前文已讲)。
bash 复制代码
adb shell dumpsys window | findstr "mCurrentFocus"
  • automationName:指定自动化引擎,Android 推荐UiAutomator2(支持最新系统版本)。

2. 元素定位与操作

自动化的核心是 "找到元素并操作"。Appium 支持多种定位方式,常用的有IDXPath文本,以下是实战中最常用的两种:

(1)通过 ID 定位(最稳定)

元素的resource-id是开发时设置的唯一标识,优先用 ID 定位:

python 复制代码
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 结合文本、属性等定位:

python 复制代码
# 定位文本为"固件版本升级"的选项
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. 等待机制:避免 "元素未加载" 问题

应用界面加载需要时间,直接定位元素可能导致 "找不到元素" 错误。需使用显式等待,等待元素处于可操作状态后再执行操作:

python 复制代码
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException

def 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 还支持输入文本、滑动、返回主页等常用操作:

python 复制代码
# 输入文本(如账号密码)
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. 关闭会话

测试结束后,需关闭会话释放资源:

python 复制代码
driver.quit()  # 关闭当前会话,释放设备

三、实战案例:实现应用登录与循环测试

基于以上基础,我们可以组合出完整的自动化场景。以下面应用为例,实现 "管理员登录" 和 "固件升级循环测试" 两个核心流程。

1. 管理员登录流程

登录是大多数应用的基础操作,需处理 "应用已打开" 和 "需重新启动应用" 两种场景:

python 复制代码
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 = 3
        for i in range(max_scrolls):
            try:
                # 定位应用图标(文本为"唐米力")
                app_icon = wait_element_clickable(
                    driver, 
                    (AppiumBy.XPATH, '//*[@text="唐米力"]'), 
                    timeout=5
                )
                app_icon.click()
                print("已启动应用")
                time.sleep(5)  # 等待应用启动
                break
            except 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. 固件升级循环测试

很多场景需要重复执行某一操作(如多次测试固件升级),可通过循环实现,并加入异常处理确保稳定性:

python 复制代码
def run_firmware_upgrade(driver, run_count=10):
    """循环执行固件升级测试"""
    stop_flag = False  # 用于标记是否停止后续测试
    for i in range(run_count):
        if stop_flag:
            print("检测到错误,停止后续升级测试")
            break
        
        try:
            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. 异常处理与截图

测试出错时,截图能帮助定位问题,可封装一个截图函数:

python 复制代码
import os
from datetime import datetime

def 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

四、自动化测试最佳实践

  1. 优先使用稳定的定位方式:ID > XPath(属性组合) > 文本(文本可能随语言变化)。
  2. 合理设置等待时间:避免用time.sleep(),多用显式等待;根据页面加载速度调整超时时间。
  3. 处理会话失效:设备断开、应用崩溃可能导致会话失效,需在关键步骤前检查会话状态(如driver.current_activity)。
  4. 循环测试加中断机制:重复测试时,一旦出现致命错误,立即停止后续执行(如上文的stop_flag),避免无效执行。
  5. 及时清理资源:测试结束后务必调用driver.quit(),释放设备连接,避免影响其他测试。

到这里我的分享就结束了,欢迎到评论区探讨交流!!

💖如果觉得有用的话还请点个赞吧 💖

相关推荐
有风南来12 天前
appium2.0+之PointerActions详解
自动化测试·appium·高级手势·pointeractions·移动设备自动化·单点触控
惜.己12 天前
appium中urllib3.exceptions.LocationValueError: No host specified. 的错误解决办法
网络·appium
惜.己12 天前
python中appium
开发语言·python·appium
TRACER~8520 天前
移动端自动化Appium框架
运维·appium·自动化
nee~1 个月前
appium
appium
amazinging1 个月前
北京-4年功能测试2年空窗-报培训班学测开-第四十四天
python·学习·appium
AIZHINAN1 个月前
Appium 简介
自动化测试·测试工具·appium
amazinging1 个月前
北京-4年功能测试2年空窗-报培训班学测开-第四十一天
python·学习·appium
amazinging1 个月前
北京-4年功能测试2年空窗-报培训班学测开-第三十九天
python·学习·appium