乐于分享
好东西不私藏

App自动化测试入门:Appium从0到第一个用例

App自动化测试入门:Appium从0到第一个用例

想象一个场景:你的App有200个页面,每次发布前都要人工测试一遍核心功能——登录、搜索、下单、支付。一个页面5分钟,光回归测试就要16个小时。

人工成本高、效率低、还容易漏测。这正是移动端测试工程师的真实困境。

今天这篇文章,带你从0开始,用Python+Appium写第一个自动化测试用例。全文实战导向,代码可直接运行,适合有Python基础的初中级测试工程师。

一、为什么移动端需要自动化?

手工测试App有三个噩梦:

① 重复劳动无尽头 —— 每次发版都要跑一遍核心流程,同一个登录页面测了上百遍,人都麻了。

② 碎片化地狱 —— Android机型、分辨率、系统版本排列组合,少则几十款,多则上百款。手动覆盖根本不现实。

③ 回归成本高 —— 改一行代码,心理负担却很大。怕影响已有功能,又没有足够时间全量回归。

App自动化测试就是来解决这些问题的——把重复的工作交给代码,一次编写,反复执行,快速反馈

而Appium,是目前最主流的跨平台移动端自动化框架。

二、Appium是什么?

2.1 概念

Appium是一个开源的跨平台自动化测试框架,支持iOS和Android原生App、混合App(H5页面)、以及移动端Web App(浏览器)。

它的核心理念是:测试人员不需要重新编译或修改App,就能自动化操作它

2.2 架构原理

Appium的架构分为三层:

  • 第一层:客户端(Client)
     —— 测试脚本用Python、Java、JavaScript等语言编写,通过WebDriver协议与Appium Server通信。
  • 第二层:Appium Server
     —— 用Node.js开发的服务器,接收客户端请求,转换成对应平台的自动化指令。
  • 第三层:设备端(Device)
     —— Android端使用UiAutomator2,iOS端使用XCUITest。

数据流向:

Python脚本 → Appium Server → UiAutomator2/XCUITest → 设备执行操作 → 逐层返回结果

2.3 核心特点

  • 跨平台
    :一套代码,同时支持Android和iOS
  • 无需侵入App
    :无需在目标App中植入任何代码,测试的是真实用户场景
  • 多语言支持
    :Python、Java、JavaScript、C#、Ruby均可
  • 生态成熟
    :社区活跃,周边工具完善

三、环境准备

步骤1:安装Android SDK

① 下载Android Studio
访问 https://developer.android.com/studio 下载,一路下一步安装。

② 启动SDK Manager
打开Android Studio → Settings → SDK Manager,勾选以下组件:

  • Android SDK Platform(选稳定版本,如Android 13 / API 33)
  • Android SDK Build-Tools
  • Android SDK Command-line Tools
  • Platform-Tools(包含adb工具)

③ 配置环境变量

ANDROID_HOME = D:\Android\Sdk
PATH中添加:%ANDROID_HOME%\platform-tools
PATH中添加:%ANDROID_HOME%\cmdline-tools\latest\bin

验证安装:

adb --version

出现版本号说明安装成功。

步骤2:安装Python + Appium库

确保已安装Python 3.8+,然后安装Appium相关依赖:

pip install appium-python-client

下载 Appium Desktop(图形界面版本,包含Server和Inspector元素定位工具):

下载地址:https://github.com/appium/appium-desktop/releases

步骤3:启动Appium Desktop

安装完成后,双击打开Appium Desktop,点击右上角 Start Server 按钮,默认端口 4723

界面显示绿色 “The server is running”,表示服务就绪。

步骤4:连接真机或模拟器

模拟器方式(推荐新手):

打开Android Studio → AVD Manager → 创建虚拟设备(如Pixel 6,Android 13)→ 启动模拟器。

真机方式:

  • 手机开启开发者选项 → 启用USB调试
  • 用数据线连接电脑,授权调试
  • 验证连接:adb devices
List of devices attached
emulator-5554   device

出现以上输出即为连接成功。

四、第一个Appium用例:打开计算器

我们以打开Android系统计算器为例,写一个完整可运行的用例。

# 导入Appium WebDriver
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
import time

# 第一步:定义Desired Capabilities(配置清单)
desired_caps = {
    "platformName": "Android",           # 目标平台
    "deviceName": "emulator-5554",      # 设备名(adb devices查到的名称)
    "appPackage": "com.android.calculator2",  # 计算器的包名
    "appActivity": "com.android.calculator2.Calculator",  # 启动的Activity
    "noReset": True,                    # 不要重置App状态
}

# 第二步:启动Appium Session(会话)
driver = webdriver.Remote(
    "http://localhost:4723/wd/hub",  # Appium Server地址
    desired_caps                         # 传入配置参数
)

# 第三步:点击数字"1"
btn_1 = driver.find_element(AppiumBy.ID, "com.android.calculator2:id/digit_1")
btn_1.click()
print("点击了数字1")

# 第四步:点击"+"
btn_plus = driver.find_element(AppiumBy.ID, "com.android.calculator2:id/op_add")
btn_plus.click()
print("点击了加号")

# 第五步:点击数字"2"
btn_2 = driver.find_element(AppiumBy.ID, "com.android.calculator2:id/digit_2")
btn_2.click()
print("点击了数字2")

# 第六步:点击"="
btn_equal = driver.find_element(AppiumBy.ID, "com.android.calculator2:id/eq")
btn_equal.click()
print("点击了等号")

# 第七步:等待3秒,观察结果
time.sleep(3)

# 第八步:关闭Session
driver.quit()
print("测试完成!")

💡 小贴士: 资源ID如 com.android.calculator2:id/digit_1,可以用Appium Inspector打开App后直接查看,不需要死记硬背。

五、核心概念详解

5.1 Desired Capabilities 配置

Desired Capabilities是Appium的”导航仪”,告诉Server要连接什么设备、启动什么App。

配置项
说明
示例值
platformName
操作系统
Android / iOS
deviceName
设备名称
emulator-5554
appPackage
App包名
com.example.app
appActivity
启动Activity
.MainActivity
platformVersion
系统版本
13
noReset
是否重置App
True / False
unicodeKeyboard
使用Unicode键盘
True(中文输入时)

5.2 元素定位方法

① resource-id 定位(最推荐)

driver.find_element(AppiumBy.ID, "com.example.app:id/btn_login")

② text 定位(ACCESSIBILITY_ID)

driver.find_element(AppiumBy.ACCESSIBILITY_ID, "登录")

③ XPath 定位(灵活但慢)

driver.find_element(AppiumBy.XPATH, "//android.widget.Button[@text='登录']")

④ UiAutomator定位(Android原生)

driver.find_element(
    AppiumBy.ANDROID_UIAUTOMATOR,
    'description("Settings")'
)

定位优先级: ID > ACCESSIBILITY_ID > XPath > UiAutomator

5.3 常见操作

# 点击
element.click()

# 输入文本
element.send_keys("hello")

# 清除输入框
element.clear()

# 向上滑动(从y=1500滑到y=500)
driver.swipe(500, 1500, 500, 500, 500)

# 隐式等待(全局)
driver.implicitly_wait(10)

# 显式等待(精确等待)
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

element = WebDriverWait(driver, 15).until(
    EC.element_to_be_clickable((AppiumBy.ID, "com.example:id/btn_login"))
)

六、实战:完整的登录测试用例

6.1 项目结构(Page Object模式)

app_auto_test/
├── base/
│   └── base_page.py           # 基础封装层
├── page/
│   ├── login_page.py          # 登录页面对象
│   └── home_page.py           # 首页对象
├── test_case/
│   └── test_login.py          # 测试用例
├── config/
│   └── desired_caps.py       # 配置
└── run.py                     # 运行入口

6.2 基础封装层(base_page.py)

# base/base_page.py
from appium.webdriver.webdriver import WebDriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from appium.webdriver.common.appiumby import AppiumBy

class BasePage:
    """所有Page Object的父类"""

    def __init__(self, driver: WebDriver):
        self.driver = driver

    def find_element(self, locator, timeout=15):
        """查找元素(带显式等待)"""
        return WebDriverWait(self.driver, timeout).until(
            EC.presence_of_element_located(locator)
        )

    def click(self, locator):
        """点击操作"""
        self.find_element(locator).click()

    def input_text(self, locator, text):
        """输入文本"""
        element = self.find_element(locator)
        element.clear()
        element.send_keys(text)

    def get_text(self, locator):
        """获取元素文本"""
        return self.find_element(locator).text

    def swipe_up(self, duration=500):
        """向上滑动"""
        size = self.driver.get_window_size()
        x = size["width"] / 2
        start_y = size["height"] * 0.75
        end_y = size["height"] * 0.25
        self.driver.swipe(x, start_y, x, end_y, duration)

6.3 登录页面对象(login_page.py)

# page/login_page.py
from base.base_page import BasePage
from appium.webdriver.common.appiumby import AppiumBy

class LoginPage(BasePage):
    """登录页面对象"""

    # ======== 元素定位器 ========
    USERNAME_INPUT = (AppiumBy.ID, "com.example.app:id/et_username")
    PASSWORD_INPUT = (AppiumBy.ID, "com.example.app:id/et_password")
    LOGIN_BUTTON   = (AppiumBy.ID, "com.example.app:id/btn_login")
    ERROR_TOAST    = (AppiumBy.XPATH, "//android.widget.Toast")

    # ======== 页面操作 ========
    def input_username(self, username):
        self.input_text(self.USERNAME_INPUT, username)

    def input_password(self, password):
        self.input_text(self.PASSWORD_INPUT, password)

    def click_login(self):
        self.click(self.LOGIN_BUTTON)

    def login(self, username, password):
        """组合操作:登录"""
        self.input_username(username)
        self.input_password(password)
        self.click_login()

    def get_error_message(self):
        """获取错误提示"""
        return self.get_text(self.ERROR_TOAST)

6.4 测试用例(test_login.py)

# test_case/test_login.py
import pytest
from appium import webdriver
from page.login_page import LoginPage

class TestLogin:

    @pytest.fixture(scope="function")
    def driver(self):
        """启动App的fixture"""
        desired_caps = {
            "platformName": "Android",
            "deviceName": "emulator-5554",
            "appPackage": "com.example.app",
            "appActivity": "com.example.app.MainActivity",
            "noReset": True,
        }
        driver = webdriver.Remote("http://localhost:4723/wd/hub", desired_caps)
        yield driver
        driver.quit()

    @pytest.fixture(scope="function")
    def login_page(self, driver):
        """登录页面fixture"""
        return LoginPage(driver)

    def test_login_success(self, login_page):
        """测试正常登录"""
        login_page.login("testuser", "password123")
        # 验证登录成功,跳转到首页
        assert "首页" in login_page.driver.page_source

    def test_login_wrong_password(self, login_page):
        """测试密码错误"""
        login_page.login("testuser", "wrongpass")
        error_msg = login_page.get_error_message()
        assert "密码错误" in error_msg

七、常见问题与解决方案

Q1:adb devices找不到设备?

  • 检查手机是否开启开发者模式和USB调试
  • 重新插拔数据线
  • 尝试换一根数据线(有些劣质线只充电不传数据)

Q2:Appium启动后连接超时?

  • 确认Appium Server已启动(绿色运行状态)
  • 确认端口4723没有被占用:netstat -ano | findstr 4723

Q3:元素定位找不到元素?

  • 检查App是否已经完成加载(加显式等待)
  • 确认resource-id是否正确(用Appium Inspector查看)
  • 确认元素在当前页面(可能需要滑动才能看到)

Q4:输入中文一直报错?

  • 在Desired Capabilities中添加:"unicodeKeyboard": True
  • 并在测试结束后恢复键盘:driver.hide_keyboard()

Q5:模拟器启动失败?

  • 确认电脑虚拟化已开启(BIOS中启用VT-x/AMD-V)
  • 确认Android Studio中的AVD配置正确
  • 尝试以管理员身份运行

— — —

写在最后

Appium是移动端自动化的入门首选。掌握它,你就掌握了Android和iOS双平台的自动化测试能力。

学习路径建议:

  • 先把环境搭起来,跑通第一个用例
  • 然后学元素定位和常见操作
  • 最后引入Page Object模式,写可维护的测试代码

记住:自动化测试的核心价值在于持续反馈,而不是写完就扔。 把它融入你的日常工作流程,才是真正的掌握。

💬 互动时间

你在学习Appium的过程中遇到过哪些坑?欢迎在留言区分享,我们一起交流!

如果这篇文章对你有帮助,点个在看,我会持续分享更多移动端测试干货 🙌