前言
Web 自动化模拟用户在 PC 浏览器上的操作,接口自动化绕过 UI 直接测后端。但还有一类测试没覆盖:用户在手机上的操作。
本篇分为两个部分:
第一部分(环境基础):搭建 Appium 测试环境,用 MallLite 移动 Web 版快速验证环境是否可用。这部分不管测移动 Web 还是原生 App,都需要。
第二部分(原生 App 知识体系):原生 App 测试的完整流程、8 种元素定位方式、常用操作(启动/安装/权限弹窗/前后台切换)。这部分是后续 15、16 篇的基础。
一、移动 Web vs 原生 App vs 混合 App
1.1 三种移动端应用类型
类型 1:移动 Web
手机浏览器打开的网页
技术栈:HTML + CSS + JavaScript
入口:浏览器地址栏输入 URL
例子:在手机 Chrome 中打开 MallLite
类型 2:原生 App(后续重点)
从应用商店下载安装的 App
技术栈:Android 用 Java/Kotlin,iOS 用 Swift/ObjC
入口:桌面图标
例子:微信、淘宝、美团
类型 3:混合 App
原生 App 的壳 + 内嵌网页(WebView)
技术栈:原生框架 + WebView 组件
例子:很多电商 App 的商品详情页其实是一个网页
1.2 三种类型的核心差异
| 维度 | 移动 Web | 原生 App | 混合 App |
|---|---|---|---|
| 被测对象 | 浏览器里的网页 | 装在手机上的应用 | 原生壳 + 内嵌网页 |
| 安装方式 | 不需要安装 | 应用商店/ADB 安装 | 应用商店/ADB 安装 |
| 元素定位 | CSS、XPath(跟 PC Web 一样) | resource-id、content-desc | 两者混合 |
| 系统权限 | 无 | 相机、定位、通讯录 | 部分有 |
| 推送通知 | 无 | 有 | 有 |
| 离线使用 | 不支持 | 支持 | 部分支持 |
| 手势操作 | 基本滑动 | 复杂手势(多指、摇一摇) | 两者混合 |
1.3 测试上的区别
移动 Web 测试:
Desired Capabilities → browserName: "Chrome"
元素定位 → CSS Selector、XPath(跟 PC Web 一样)
启动方式 → 打开浏览器,输入 URL
本篇用 MallLite 快速验证
原生 App 测试:
Desired Capabilities → app: "xxx.apk"(或 app_package + app_activity)
元素定位 → resource-id、content-desc、accessibility_id
启动方式 → 启动 App
后续 15、16 篇重点讲
二、Appium 工作原理
2.1 整体架构
你的 Python 测试脚本
│
│ ① 发送 HTTP 请求(WebDriver 协议)
│ 例:POST /session/xxx/element/click
│
▼
Appium Server(运行在你的电脑上,端口 4723)
│
│ ② 解析命令,转发给对应的 Driver
│
▼
UiAutomator2 Driver(Android 自动化引擎)
│
│ ③ 通过 ADB 跟手机通信
│
▼
Android 模拟器 / 真机(执行操作)
│
│ ④ 返回结果
▼
逐层返回给你的 Python 脚本
知识点:为什么多了一层 Appium Server?
直接通过 ADB 也能控制手机(比如 adb shell input tap 100 200),但这样做有三个问题:
- 每个命令都要手动拼 ADB 参数,写起来很繁琐
- Android 和 iOS 的命令完全不同,无法统一
- 没有元素查找能力(只能用坐标点击,页面一改位置就失效)
Appium Server 的价值是提供统一的 WebDriver 协议,让你用同一套 API 控制不同平台的设备。
2.2 跟 Selenium 的关系
如果你学过 Selenium,Appium 上手会很快,因为两者的协议一样:
Selenium: 你的代码 → WebDriver 协议 → ChromeDriver → PC Chrome 浏览器
Appium: 你的代码 → WebDriver 协议 → Appium Server → UiAutomator2 → 手机
代码层面几乎一样:
python
# Selenium(PC 浏览器)
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("http://example.com")
driver.find_element(By.ID, "login").click()
# Appium(手机浏览器或 App)
from appium import webdriver
driver = webdriver.Remote("http://localhost:4723", options=...)
driver.get("http://example.com")
driver.find_element(AppiumBy.ID, "login").click()
区别只在:Selenium 的 Driver 是本地的(ChromeDriver),Appium 的 Driver 是远程的(通过 HTTP 连接 Appium Server)。
三、环境搭建
3.1 总览
| 工具 | 在哪安装 | 作用 |
|---|---|---|
| Java JDK | 独立下载 | Android SDK 依赖 |
| Android SDK | Android Studio(自带) | ADB + 模拟器 |
| Node.js | 独立下载 | Appium Server 依赖 |
| Appium Server | 系统终端,用 npm 安装 | 自动化中转站 |
| Appium Inspector | 独立下载 | 元素查看器 |
| Appium-Python-Client | PyCharm 终端,用 pip 安装 | Python 调用 Appium |
知识点:各工具之间的依赖关系
你的 Python 测试脚本
↓ import
Appium-Python-Client(Python 库,pip 安装)
↓ HTTP 请求
Appium Server(独立服务,npm 安装)
↓ 依赖
Node.js(运行时环境)
UiAutomator2 Driver(Appium 驱动)
↓ 通过
Android SDK + ADB(调试桥)
↓ 依赖
Java JDK(运行时环境)
↓ 控制
Android 模拟器 / 真机
3.2 安装步骤
第 1 步:安装 Java JDK
在系统 PowerShell 中执行(不是 PyCharm 终端):
powershell
scoop install openjdk17
或者手动下载:浏览器打开 https://adoptium.net/,下载 JDK 17 安装包,双击安装。
验证:
powershell
java -version
第 2 步:安装 Android Studio(含 SDK 和模拟器)
1. 浏览器打开 https://developer.android.com/studio
2. 下载安装包,双击安装
3. 安装过程中会自动下载 Android SDK
4. 安装完成后打开 Android Studio
5. Tools → Device Manager → Create Device
6. 选择 Pixel 6 → Next
7. 选择 API 34 (Android 14) → Next → Finish
设置环境变量(在 Windows 系统设置中):
系统变量新增:
ANDROID_HOME = C:\Users\你的用户名\AppData\Local\Android\Sdk
Path 中追加:
%ANDROID_HOME%\platform-tools
%ANDROID_HOME%\emulator
验证:重新打开 PowerShell,执行:
powershell
adb version
第 3 步:安装 Node.js
浏览器打开 https://nodejs.org/,下载 LTS 版本,双击安装。
验证:
powershell
node -v
npm -v
第 4 步:安装 Appium Server 和驱动
在系统 PowerShell 中执行:
powershell
npm install -g appium
appium driver install uiautomator2
验证:
powershell
appium --version
第 5 步:安装 Appium Inspector
浏览器打开 https://github.com/appium/appium-inspector/releases,下载 .exe 安装包,双击安装。
第 6 步:安装 Appium Python Client
在 PyCharm 终端中执行(跟安装 requests、pytest 一样):
powershell
pip install Appium-Python-Client
3.3 安装顺序总结
第 1 步:系统 PowerShell → scoop install openjdk17
第 2 步:浏览器 → 下载 Android Studio → 安装 → 创建模拟器
第 3 步:浏览器 → 下载 Node.js → 安装
第 4 步:系统 PowerShell → npm install -g appium
第 5 步:系统 PowerShell → appium driver install uiautomator2
第 6 步:浏览器 → 下载 Appium Inspector → 安装
第 7 步:PyCharm 终端 → pip install Appium-Python-Client
前一步失败会影响后面,按顺序来。
四、ADB 常用命令
4.1 什么是 ADB
ADB(Android Debug Bridge)是 Android SDK 提供的命令行工具,用来控制 Android 设备或模拟器。
知识点:ADB 在 App 自动化中的三个用途
| 用途 | 场景 | 命令 |
|---|---|---|
| 找 App 信息 | 原生 App 测试需要包名和 Activity | adb shell dumpsys activity |
| 截图排查 | 用例失败时截图看当时屏幕状态 | adb shell screencap |
| 安装卸载 | 原生 App 需要先安装 APK | adb install / adb uninstall |
4.2 设备管理
powershell
# 查看连接的设备
adb devices
# 输出:
# List of devices attached
# emulator-5554 device ← 模拟器
# 192.168.1.100:5555 device ← WiFi 连接的真机
# 启动模拟器
emulator -avd Pixel_6_API_34
4.3 应用管理
powershell
# 安装 APK(原生 App 测试必须)
adb install path/to/app.apk
# 卸载 App
adb uninstall com.example.app
# 查看当前运行的 App(找包名和 Activity)
adb shell dumpsys activity activities | findstr mResumedActivity
# 输出示例:
# mResumedActivity: ActivityRecord{xxx com.example.app/.MainActivity u0}
# 包名: com.example.app Activity: .MainActivity
知识点:找包名和 Activity 是原生 App 测试的第一步
原生 App 测试的 Desired Capabilities 需要填 app_package 和 app_activity。获取方式:
方式 1:打开你想测的 App,然后执行
adb shell dumpsys activity activities | findstr mResumedActivity
方式 2:如果知道 APK 文件,用 aapt 工具查看
aapt dump badging app.apk | findstr package
aapt dump badging app.apk | findstr launchable-activity
方式 3:问开发同学要
4.4 截图和文件
powershell
# 截图并保存到电脑
adb shell screencap /sdcard/screenshot.png
adb pull /sdcard/screenshot.png .
# 录屏(Ctrl+C 停止)
adb shell screenrecord /sdcard/record.mp4
adb pull /sdcard/record.mp4 .
# 推送文件到手机
adb push local_file.txt /sdcard/
# 从手机拉取文件
adb pull /sdcard/file.txt .
4.5 设备信息
powershell
# 设备型号
adb shell getprop ro.product.model
# Android 版本
adb shell getprop ro.build.version.release
# 屏幕分辨率
adb shell wm size
4.6 日志查看
powershell
# 实时查看手机所有日志
adb logcat
# 只看特定关键词
adb logcat | findstr chrome
# 清除日志
adb logcat -c
五、模拟器 vs 真机
| 维度 | 模拟器 | 真机 |
|---|---|---|
| 成本 | 免费 | 需要购买 |
| 速度 | 取决于电脑性能 | 取决于手机性能 |
| 稳定性 | 稳定 | 可能被电话、通知打断 |
| 操作调试 | 鼠标操作,方便 | 手指操作 |
| 网络访问电脑 | 10.0.2.2 |
电脑局域网 IP |
| 覆盖度 | 一种设备配置 | 不同品牌、型号 |
| GPS/相机/传感器 | 需要模拟 | 真实硬件 |
| 适合场景 | 开发调试、CI/CD | 兼容性测试、验收测试 |
知识点:模拟器中 localhost 不是你的电脑
模拟器运行在独立的虚拟网络中:
localhost 在模拟器中 = 模拟器自己(不是你的电脑)
正确的访问方式:
Android 官方模拟器:http://10.0.2.2:8000 ← 特殊地址,指向宿主机
Genymotion 模拟器: http://10.0.3.2:8000
真机(同一 WiFi): http://电脑局域网IP:8000 ← 如 192.168.1.100:8000
查找电脑局域网 IP:
powershell
ipconfig
# 找到 "无线局域网适配器 WLAN" 或 "以太网"
# IPv4 地址:192.168.1.xxx
六、Appium Inspector
6.1 它是什么
Appium Inspector 是一个桌面工具,用来查看手机屏幕上的元素及其属性。类似 PC 浏览器的 F12 开发者工具,只不过是给手机用的。
6.2 启动流程
第 1 步:启动 Appium Server
系统 PowerShell → 执行 appium
第 2 步:启动模拟器
Android Studio → Device Manager → 启动
或:emulator -avd Pixel_6_API_34
第 3 步:打开 Appium Inspector
双击桌面图标
6.3 两种模式的配置
移动 Web 模式(快速验证用):
json
{
"platformName": "Android",
"appium:automationName": "UiAutomator2",
"appium:browserName": "Chrome",
"appium:noReset": true
}
原生 App 模式(后续篇会用):
json
{
"platformName": "Android",
"appium:automationName": "UiAutomator2",
"appium:appPackage": "com.example.app",
"appium:appActivity": ".MainActivity",
"appium:noReset": false
}
知识点:browserName 和 appPackage 二选一
| 填了什么 | Appium 的行为 |
|---|---|
browserName: Chrome |
打开浏览器 → 移动 Web 模式 |
appPackage + appActivity |
启动 App → 原生 App 模式 |
| 两个都填 | 行为不确定,不要这么做 |
6.4 使用方式
连接成功后,左侧面板显示元素树,右侧面板显示属性。操作方式:
| 功能 | 说明 |
|---|---|
| 点击元素树中的节点 | 右侧显示该元素的属性 |
| Tap 按钮 | 在手机上模拟点击选中的元素 |
| Swipe 按钮 | 模拟滑动 |
| 地址栏输入 URL | 导航到指定网页(移动 Web 模式) |
| 刷新按钮 | 刷新元素树(页面变化后需要刷新) |
七、Desired Capabilities 详解
7.1 什么是 Desired Capabilities
Desired Capabilities 是一组键值对,告诉 Appium Server 你想控制什么样的设备。可以理解为"设备配置清单"。
知识点:为什么叫"Desired"(期望的)?
因为你描述的是你"期望"的设备环境。如果实际设备不满足这些条件,Appium 会报错。比如你期望 platformVersion: 14,但设备是 Android 12,Appium 会提示版本不匹配。
7.2 通用 Capabilities
python
options = UiAutomator2Options()
# ===== 平台信息 =====
options.platform_name = "Android" # 必填,操作系统
options.platform_version = "14" # 可选,Android 版本(不填自动检测)
options.device_name = "Pixel_6" # 可选,设备名(不填用第一个连接的设备)
options.automation_name = "UiAutomator2" # 推荐,自动化引擎
# ===== 会话控制 =====
options.no_reset = True # 不重置 App 状态(保留登录态等)
options.full_reset = False # 不完全重置(不卸载 App)
options.new_command_timeout = 300 # 无操作超时(秒),默认 60
7.3 移动 Web 专用 Capabilities
python
options.browser_name = "Chrome" # 必填,使用浏览器
# 不填 app 相关参数
7.4 原生 App 专用 Capabilities
python
# 方式 1:指定 APK 文件(Appium 会自动安装到设备上)
options.app = "/path/to/app.apk"
# 方式 2:指定包名和 Activity(App 已经装在设备上了)
options.app_package = "com.example.app"
options.app_activity = ".MainActivity"
# 方式 3:两个都填(指定 APK,同时指定启动哪个 Activity)
options.app = "/path/to/app.apk"
options.app_package = "com.example.app"
options.app_activity = ".MainActivity"
7.5 什么时候需要改 Capabilities
| 场景 | 改什么 |
|---|---|
| 测试移动 Web | browser_name = "Chrome",不填 app |
| 测试原生 App | 填 app_package + app_activity,不填 browser_name |
| 测试不同 Android 版本 | platform_version |
| 用例中途超时断开 | new_command_timeout 调大 |
| 要清浏览器缓存 | no_reset = False |
| 连接真机 | device_name 填真机名称 |
八、原生 App 测试流程
从本节开始进入原生 App 的知识体系。后续 15、16 篇会基于这些知识搭建框架和编写用例。
8.1 原生 App 测试的完整流程
第 1 步:获取 App 信息
拿到 APK 文件,或获取包名和 Activity
用 ADB 命令或问开发同学
第 2 步:安装 App 到设备
adb install app.apk
或在 Capabilities 中填 app 路径让 Appium 自动安装
第 3 步:配置 Desired Capabilities
填 app_package 和 app_activity(或 app 路径)
第 4 步:用 Appium Inspector 定位元素
打开 App,找到你需要操作的元素的属性
记录 resource-id、content-desc、XPath 等
第 5 步:编写测试脚本
用第 4 步记录的属性编写 Python 代码
第 6 步:运行和调试
pytest 运行,失败时用 ADB 截图和日志排查
第 7 步:持续集成
把脚本集成到 CI/CD 流程中
知识点:原生 App 测试比移动 Web 多了哪些步骤?
| 步骤 | 移动 Web | 原生 App |
|---|---|---|
| 获取 App 信息 | 不需要(直接输入 URL) | 需要包名和 Activity |
| 安装 App | 不需要 | 需要安装 APK |
| 元素定位 | CSS/XPath(跟 PC 一样) | resource-id/content-desc(完全不同) |
| 权限处理 | 不涉及 | 需要处理系统权限弹窗 |
| 前后台切换 | 不涉及 | 可能被电话/通知打断 |
九、原生 App 元素定位方式
9.1 移动 Web vs 原生 App 的定位方式对比
移动 Web(跟 PC Web 一样):
CSS Selector → driver.find_element(AppiumBy.CSS_SELECTOR, "#login-btn")
XPath → driver.find_element(AppiumBy.XPATH, "//a[@class='product']")
TAG_NAME → driver.find_element(AppiumBy.TAG_NAME, "a")
原生 App(完全不同的体系):
ID → driver.find_element(AppiumBy.ID, "com.app:id/btn_login")
ACCESSIBILITY_ID → driver.find_element(AppiumBy.ACCESSIBILITY_ID, "登录")
CLASS_NAME → driver.find_element(AppiumBy.CLASS_NAME, "android.widget.Button")
ANDROID_UIAUTOMATOR → driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, '...')
9.2 8 种定位方式详解
以一个虚构的电商 App 登录页面为例,讲解每种定位方式:
xml
<!-- 登录页面的元素结构(简化版) -->
<LinearLayout>
<EditText resource-id="com.malllite.app:id/et_username"
text="" hint="请输入用户名" class="android.widget.EditText" />
<EditText resource-id="com.malllite.app:id/et_password"
text="" hint="请输入密码" class="android.widget.EditText" />
<Button resource-id="com.malllite.app:id/btn_login"
text="登录" content-desc="登录按钮" class="android.widget.Button" />
<TextView resource-id="com.malllite.app:id/tv_register"
text="还没有账号?立即注册" content-desc="注册链接" class="android.widget.TextView" />
</LinearLayout>
方式 1:resource-id(最常用)
python
# resource-id 是原生 App 元素的唯一标识符(类似 HTML 的 id 属性)
# 格式:包名:resource-id/资源名
driver.find_element(AppiumBy.ID, "com.malllite.app:id/btn_login").click()
driver.find_element(AppiumBy.ID, "com.malllite.app:id/et_username").send_keys("admin")
知识点:resource-id 是原生 App 定位的首选
跟 HTML 的 id 一样,resource-id 通常是唯一的,定位速度快、稳定性好。优先用这个。
方式 2:text
python
# 用元素的 text 属性定位
# 注意:text 是精确匹配,不是模糊匹配
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("登录")').click()
知识点:Appium 没有直接的 AppiumBy.TEXT
text 定位需要用 ANDROID_UIAUTOMATOR 引擎,这是 Android 特有的定位方式。写法比较特殊:
python
# 包含文本
'driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, \'new UiSelector().textContains("登录")\')'
# 以文本开头
'driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, \'new UiSelector().textStartsWith("还没有")\')'
# 文本匹配正则表达式
'driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, \'new UiSelector().textMatches(".*注册.*")\')'
方式 3:content-desc / accessibility_id
python
# content-desc 是无障碍描述(类似 HTML 的 aria-label)
# 用 AppiumBy.ACCESSIBILITY_ID 定位
driver.find_element(AppiumBy.ACCESSIBILITY_ID, "登录按钮").click()
driver.find_element(AppiumBy.ACCESSIBILITY_ID, "注册链接").click()
知识点:content-desc 是什么?
Android 的无障碍服务(如 TalkBack 朗读屏幕内容)会读出 content-desc 的值。开发应该给每个可交互元素设置有意义的 content-desc。它不是必填的,有些元素可能没有。
方式 4:class_name
python
# 用 Android 类名定位
# 常见类名:
# android.widget.Button → 按钮
# android.widget.EditText → 输入框
# android.widget.TextView → 文本
# android.widget.ImageView → 图片
# android.widget.ListView → 列表
# 通常不单独用(同类型元素太多),配合其他条件
buttons = driver.find_elements(AppiumBy.CLASS_NAME, "android.widget.Button")
方式 5:xpath
python
# XPath 跟 PC Web 的思路一样,但语法不同
# 原生 App 的 XPath 用 Android 类名代替 HTML 标签名
# 找第一个 Button
driver.find_element(AppiumBy.XPATH, "//android.widget.Button")
# 找 text="登录" 的 Button
driver.find_element(AppiumBy.XPATH, '//android.widget.Button[@text="登录"]')
# 找 resource-id 包含 "login" 的元素
driver.find_element(AppiumBy.XPATH, '//*[contains(@resource-id, "login")]')
知识点:XPath 是"最后手段"
XPath 定位慢、容易受页面结构变化影响。优先用 resource-id 和 content-desc,实在找不到再用 XPath。
方式 6:android_uiautomator
python
# UiAutomator 是 Android 原生的 UI 测试框架
# Appium 底层就是用它来操作 Android 的
# 可以直接写 UiAutomator 的查找表达式
# 按 resource-id 查找
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().resourceId("com.malllite.app:id/btn_login")')
# 按 class + text 组合查找
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().className("android.widget.Button").text("登录")')
# 按条件组合(AND)
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().resourceId("com.malllite.app:id/btn_login").enabled(true)')
# 在子元素中查找
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
'new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().text("查看更多"))')
知识点:UiAutomator 表达式是 Android 独有的
这是 Android 测试中最强的定位方式,支持各种条件组合和滚动查找。写法复杂,但在其他方式找不到元素时非常有用。
方式 7:android_data_matcher / android_view_matcher(Espresso 引擎)
python
# Espresso 是 Google 官方的 Android UI 测试框架
# 如果 automationName 设为 "Espresso"(不是 UiAutomator2),可以用这种方式
# 实际项目中较少使用,了解即可
方式 8:css_selector(仅限 WebView)
python
# 只在混合 App 的 WebView 部分有效
# WebView 内嵌的是网页,所以可以用 CSS 定位
# 原生部分不能用
driver.find_element(AppiumBy.CSS_SELECTOR, "#login-btn")
9.3 定位方式选择优先级
第 1 选择:resource-id → 唯一、稳定、快
第 2 选择:content-desc → 语义清晰、无障碍友好
第 3 选择:text(UiSelector) → 简单直观,但可能有重复
第 4 选择:class + 条件组合 → 需要用 UiAutomator 表达式
第 5 选择:xpath → 最灵活但最慢,最后手段
仅 WebView:css_selector → 只在混合 App 的网页部分有效
十、原生 App 常用操作
10.1 启动和安装
python
# 方式 1:在 Capabilities 中指定 APK,Appium 自动安装并启动
options.app = "/path/to/app.apk"
# 方式 2:App 已经装在设备上,通过包名启动
options.app_package = "com.malllite.app"
options.app_activity = ".MainActivity"
# 方式 3:通过 ADB 安装(不在 Capabilities 中填 app)
# adb install app.apk
# 然后在 Capabilities 中填 app_package 和 app_activity
# 方式 4:通过 ADB 启动(手动控制启动时机)
# adb shell am start -n com.malllite.app/.MainActivity
10.2 处理系统权限弹窗
Android 系统会在 App 首次使用某些功能时弹出权限请求(如相机、定位、存储)。
python
# 方式 1:在 Capabilities 中自动授予所有权限
options.auto_grant_permissions = True
# 方式 2:代码中处理弹窗
try:
# 查找"允许"按钮并点击
allow_btn = driver.find_element(AppiumBy.ID, "com.android.permissioncontroller:id/permission_allow_button")
allow_btn.click()
except:
# 没有弹窗,继续
pass
# 方式 3:用 ADB 授予权限(启动 App 之前执行)
# adb shell pm grant com.malllite.app android.permission.CAMERA
# adb shell pm grant com.malllite.app android.permission.ACCESS_FINE_LOCATION
知识点:权限弹窗是原生 App 测试中最常见的"干扰"
如果没有处理权限弹窗,用例会在弹窗处卡住直到超时。推荐在 Capabilities 中设置 auto_grant_permissions = True,让 Appium 自动处理。
10.3 前后台切换
python
# 将 App 放到后台(模拟用户按 Home 键)
driver.background_app(5) # 后台 5 秒后自动回到前台
# 将 App 放到后台(不自动回来)
driver.background_app(-1) # -1 表示不自动回来
# 手动回到前台
driver.activate_app("com.malllite.app")
知识点:为什么要测前后台切换?
用户在使用 App 时可能被电话、微信通知、系统提醒打断,App 被切到后台。恢复前台后,App 的状态应该正确(不崩溃、不丢失数据、页面状态保持)。
10.4 安装和卸载
python
# 检查 App 是否已安装
is_installed = driver.is_app_installed("com.malllite.app")
# 安装 App
driver.install_app("/path/to/app.apk")
# 卸载 App
driver.remove_app("com.malllite.app")
10.5 获取设备信息
python
# 获取设备信息
info = driver.capabilities
print(info['platformName']) # Android
print(info['deviceName']) # Pixel_6
# 获取当前 Activity
current = driver.current_activity
print(current) # .MainActivity
10.6 获取页面源码
python
# 获取当前页面的 XML 源码(类似查看 HTML 源码)
# 原生 App 的页面结构是 XML 格式
source = driver.page_source
print(source)
知识点:什么时候用 page_source?
当 Appium Inspector 定位不到某个元素时,可以通过 page_source 获取完整的页面 XML 结构,在 XML 文本中搜索元素的属性。这是一个很有用的调试手段。
十一、快速验证:移动 Web 测试脚本
本节用 MallLite 移动 Web 版快速验证环境是否正确。只写 2 条用例,重点是验证环境,不是测业务。
11.1 项目结构
powershell
# 在 PyCharm 终端中执行
New-Item -ItemType Directory -Path "app\test_cases" -Force
New-Item -ItemType Directory -Path "app\reports" -Force
New-Item -ItemType File -Path "app\test_cases\__init__.py" -Force
app/
├── conftest.py
├── pytest.ini
├── requirements.txt
├── test_cases/
│ ├── __init__.py
│ └── test_mobile_web.py
└── reports/
11.2 pytest.ini
【新建文件】 app/pytest.ini:
ini
[pytest]
testpaths = test_cases
addopts = -v --tb=short
log_cli = true
log_cli_level = INFO
log_cli_format = %(asctime)s | %(levelname)-8s | %(name)-12s | %(message)s
log_cli_date_format = %H:%M:%S
11.3 requirements.txt
【新建文件】 app/requirements.txt:
Appium-Python-Client>=4.0.0
pytest>=8.0.0
11.4 conftest.py
【新建文件】 app/conftest.py:
python
"""
App 自动化 - conftest.py
管理 WebDriver 的创建和销毁
"""
import pytest
from appium import webdriver
from appium.options.android import UiAutomator2Options
@pytest.fixture
def driver():
"""
创建 Appium WebDriver(连接手机 Chrome)
生命周期:每个用例创建一个,用例结束后关闭
"""
options = UiAutomator2Options()
options.platform_name = "Android"
options.browser_name = "Chrome"
options.no_reset = True
appium_server = "http://localhost:4723"
driver = webdriver.Remote(appium_server, options=options)
driver.implicitly_wait(10)
yield driver
driver.quit()
@pytest.fixture
def malllite_url():
"""
MallLite 的 URL(模拟器用 10.0.2.2 访问宿主机)
"""
return "http://10.0.2.2:8000"
11.5 测试用例
【新建文件】 app/test_cases/test_mobile_web.py:
python
"""
移动 Web 快速验证(验证环境是否可用)
只写 2 条用例,目的是验证环境搭建是否成功。
详细的内容留给后续篇。
"""
import time
import pytest
from appium.webdriver.common.appiumby import AppiumBy
class TestMobileWebEnvCheck:
"""环境验证"""
def test_page_loads(self, driver, malllite_url):
"""
验证 1:MallLite 页面能正常加载
打开 MallLite 首页,验证页面有内容。
如果这条通过,说明 Appium + 模拟器 + Chrome + 网络全部正常。
"""
driver.get(malllite_url)
# 获取页面标题
title = driver.title
assert title is not None, "页面标题为空,可能加载失败"
# 获取页面内容
body = driver.find_element(AppiumBy.TAG_NAME, "body")
body_text = body.text
assert len(body_text) > 0, "页面内容为空"
print(f"\n页面标题:{title}")
print(f"页面内容前 200 字:{body_text[:200]}")
def test_click_navigates(self, driver, malllite_url):
"""
验证 2:点击链接能跳转
找到页面中的第一个链接并点击,验证 URL 发生了变化。
如果这条通过,说明元素查找和点击操作正常。
"""
driver.get(malllite_url)
links = driver.find_elements(AppiumBy.TAG_NAME, "a")
if len(links) > 0:
url_before = driver.current_url
links[0].click()
time.sleep(2)
url_after = driver.current_url
assert url_after is not None
print(f"\n点击前:{url_before}")
print(f"点击后:{url_after}")
else:
pytest.skip("页面中没有找到链接元素")
11.6 运行
powershell
# 终端 1:启动 Appium Server
appium
# 终端 2:启动模拟器(或通过 Android Studio 启动)
emulator -avd Pixel_6_API_34
# 终端 3:启动 MallLite
cd mall-lite
python run.py
# 终端 4:运行测试
cd app
pytest -v
十二、运行验证与问题排查
12.1 环境验证清单
在运行测试之前,逐项检查:
□ java -version → 显示 JDK 版本
□ adb version → 显示 ADB 版本
□ adb devices → 显示连接的设备(emulator-xxx)
□ node -v → 显示 Node.js 版本
□ appium --version → 显示 Appium 版本
□ appium driver list → 显示已安装的驱动(含 uiautomator2)
□ pip show Appium-Python-Client → 显示 Python Client 版本
全部通过后再运行测试。
12.2 常见问题排查
| 问题 | 可能原因 | 解决办法 |
|---|---|---|
Connection refused: 4723 |
Appium Server 没启动 | 系统终端执行 appium |
No device found |
模拟器没启动 | adb devices 检查 |
Chrome not found |
模拟器没有 Chrome | 模拟器中安装 Chrome |
| 页面加载空白 | URL 用了 localhost | 改成 10.0.2.2 |
JAVA_HOME not set |
Java 没装或环境变量没设 | 重装 Java,设置环境变量 |
UiAutomator2 not found |
没装驱动 | appium driver install uiautomator2 |
Session creation timeout |
模拟器卡了 | 重启模拟器 |
| 测试很慢 | 模拟器性能不足 | 给模拟器分配更多内存 |
12.3 用 ADB 截图排查失败
测试失败时,手动截图查看当时的屏幕状态:
powershell
adb shell screencap /sdcard/debug.png
adb pull /sdcard/debug.png .
# 打开 debug.png 查看
十三、本篇新增文件
| 文件 | 说明 |
|---|---|
app/pytest.ini |
App 自动化运行配置 |
app/conftest.py |
WebDriver fixture |
app/requirements.txt |
依赖 |
app/test_cases/__init__.py |
包标记 |
app/test_cases/test_mobile_web.py |
2 条环境验证用例 |
十四、今日成果
- 讲解了移动 Web、原生 App、混合 App 三种类型的核心差异
- 理解了 Appium 的工作原理(Python → Appium Server → Driver → 设备)
- 搭建了完整的 Appium 测试环境,明确了每个工具在哪安装
- 掌握了 ADB 常用命令(设备管理、应用管理、截图、文件、日志)
- 了解了模拟器和真机的优劣对比和网络差异
- 学会了用 Appium Inspector 定位元素(移动 Web 和原生 App 两种模式)
- 搞懂了 Desired Capabilities 的每个参数含义
- 系统学习了原生 App 测试的完整流程(7 步)
- 掌握了 8 种元素定位方式(resource-id、text、content-desc、class、xpath、uiautomator、espresso、css)及优先级
- 学会了原生 App 的常用操作(安装、启动、权限弹窗、前后台切换)
- 用 MallLite 移动 Web 快速验证了环境
十五、下篇预告
15 - App 自动化框架搭建
下一篇搭建完整的原生 App 自动化框架:Driver Manager(管理 Appium 连接)、BasePage(封装滑动/长按/权限弹窗等原生操作)、页面对象实现(登录页/首页/商品页/购物车页)。
App 自动化篇进度:14/18。