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。
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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的过程中遇到过哪些坑?欢迎在留言区分享,我们一起交流!
如果这篇文章对你有帮助,点个在看,我会持续分享更多移动端测试干货 🙌
夜雨聆风