文章目录
- 移动APP测试实战
-
- [一、ADB:Android 调试](#一、ADB:Android 调试)
-
- [1.1 环境准备](#1.1 环境准备)
- [1.2 安装与卸载](#1.2 安装与卸载)
- [1.3 文件与日志](#1.3 文件与日志)
- [1.4 应用管理](#1.4 应用管理)
- [1.5 专项操作](#1.5 专项操作)
- [1.6 ADB 常用场景速查](#1.6 ADB 常用场景速查)
- [二、Charles:抓包 + 弱网](#二、Charles:抓包 + 弱网)
-
- [2.1 HTTPS 抓包完整配置](#2.1 HTTPS 抓包完整配置)
- [2.2 抓包实战技巧](#2.2 抓包实战技巧)
- [2.3 弱网模拟配置](#2.3 弱网模拟配置)
- 三、PerfDog:性能测试
-
- [3.1 安装和连接](#3.1 安装和连接)
- [3.2 核心指标解读](#3.2 核心指标解读)
- [3.3 实战:检测内存泄漏](#3.3 实战:检测内存泄漏)
- [3.4 标签功能(打点记录关键操作)](#3.4 标签功能(打点记录关键操作))
- 四、Appium:UI自动化
-
- [4.1 环境搭建](#4.1 环境搭建)
- [4.2 编写第一个测试](#4.2 编写第一个测试)
- [4.3 元素定位实战](#4.3 元素定位实战)
- [4.4 Page Object 模式](#4.4 Page Object 模式)
- [4.5 运行测试](#4.5 运行测试)
- [五、JMeter 后端性能压测](#五、JMeter 后端性能压测)
-
- [5.1 创建测试计划](#5.1 创建测试计划)
- 六、一个版本的测试周期
- 七、常用命令速查总表
移动APP测试实战
ADB命令调试、Charles抓包与弱网模拟、PerfDog性能监测、Appium自动化编写与执行、Battery Historian耗电分析、JMeter后端压测
一、ADB:Android 调试
ADB(Android Debug Bridge)是连接电脑和 Android 设备的桥梁。不管使用什么测试工具,底层都在调 ADB。能欧掌握ADB就是掌握了一切 Android 调试的入口。
1.1 环境准备
第一步:下载 Platform Tools
第二步:手机端开启 USB 调试
设置 → 关于手机 → 连续点击"版本号"7次 → 开启"开发者选项"
→ 开发者选项 → 开启"USB调试"
→ 连接电脑 → 手机上弹出"允许USB调试" → 勾选"始终允许" → 确定
第三步:验证连接
bash
# 查看已连接设备
adb devices
# 输出示例:
# List of devices attached
# 8BDAX00A32 device ← 正常连接
# 8BDAX00A32 unauthorized ← 手机上未点"允许"
# 8BDAX00A32 offline ← 设备离线或ADB服务挂了
ADB 服务管理:
bash
adb kill-server # 杀掉ADB服务(连接异常时先kill再start)
adb start-server # 启动ADB服务
adb reconnect # 重新连接设备(免拔线)
1.2 安装与卸载
bash
# 安装APK
adb install app.apk # 普通安装
adb install -r app.apk # 覆盖安装(保留数据)
adb install -d app.apk # 降级安装
adb install -g app.apk # 安装时自动授予所有权限
# 卸载
adb uninstall com.example.app # 卸载
adb uninstall -k com.example.app # 卸载但保留数据和缓存
1.3 文件与日志
bash
# 文件传输
adb push local_file /sdcard/ # 电脑→手机
adb pull /sdcard/remote_file ./ # 手机→电脑
# 截图和录屏
adb shell screencap /sdcard/screen.png # 截图到手机
adb pull /sdcard/screen.png # 拉到电脑
adb shell screenrecord /sdcard/video.mp4 # 录屏(Ctrl+C停止)
# 日志查看
adb logcat # 实时日志(刷屏)
adb logcat -c # 清空日志缓冲区
adb logcat -s TAG # 只看指定TAG的日志
adb logcat | grep "ERROR" # 过滤ERROR日志
adb logcat -v time > crash_log.txt # 带时间戳保存到文件
adb logcat -b crash # 只看崩溃日志
# APP专属日志
adb logcat --pid=$(adb shell pidof com.example.app) # 只看目标APP的日志
1.4 应用管理
bash
# 查看已安装应用列表
adb shell pm list packages # 所有应用
adb shell pm list packages | grep "某个关键词" # 过滤
# 查看当前前台应用
adb shell dumpsys window | grep mCurrentFocus
# 启动应用
adb shell am start -n com.example.app/.MainActivity
# 停止应用(强制杀掉)
adb shell am force-stop com.example.app
# 清除应用数据(等同于"清除缓存+清除数据")
adb shell pm clear com.example.app
1.5 专项操作
bash
# 模拟输入
adb shell input text "13800138000" # 输入文字
adb shell input keyevent 4 # 按返回键(KEYCODE_BACK)
adb shell input keyevent 3 # 按Home键
adb shell input keyevent 26 # 电源键(锁屏/唤醒)
adb shell input tap 500 1500 # 点击坐标(x,y)
adb shell input swipe 500 1500 500 500 # 滑动(startX,startY,endX,endY)
# 模拟系统事件
adb shell am broadcast -a android.intent.action.BATTERY_LOW # 低电量广播
# 查看系统信息
adb shell getprop ro.build.version.sdk # SDK版本
adb shell getprop ro.build.version.release # 系统版本号
adb shell wm size # 屏幕分辨率
adb shell wm density # 屏幕密度DPI
adb shell dumpsys battery # 电池信息
# 修改系统设置(测试用)
adb shell settings put global airplane_mode_on 1 # 开飞行模式
adb shell settings put global airplane_mode_on 0 # 关飞行模式
# Monkey 随机压力测试
adb shell monkey -p com.example.app -v 500 # 随机500次操作
adb shell monkey -p com.example.app -v --throttle 300 500 # 间隔300ms
1.6 ADB 常用场景速查
| 测试场景 | ADB 命令 |
|---|---|
| 安装测试包 | adb install -r app-debug.apk |
| 卸载+清理 | adb uninstall 包名 && adb shell pm clear 包名 |
| 看实时日志 | `adb logcat |
| 截图存证 | adb shell screencap /sdcard/bug.png && adb pull /sdcard/bug.png |
| 模拟返回键 | adb shell input keyevent 4 |
| 查看当前页面 | `adb shell dumpsys window |
| 强制杀APP | adb shell am force-stop 包名 |
| 模拟低电量 | adb shell dumpsys battery set level 15 |
| 恢复电量 | adb shell dumpsys battery reset |
二、Charles:抓包 + 弱网
Charles 是 APP 测试的必备工具,注意:看请求内容和模拟弱网。
2.1 HTTPS 抓包完整配置
第一步:电脑端安装并信任证书
第二步:开启 SSL Proxying
第三步:手机配置代理
第四步:手机安装证书
iOS 额外步骤:
设置 → 通用 → 关于本机 → 证书信任设置 → 开启 Charles 证书
验证是否成功:打开爱听外语APP随便操作,在 Charles 中应能看到 HTTPS 请求且 Contents 标签有明文 JSON 数据。
2.2 抓包实战技巧
过滤请求:
Charles 左下角 Filter 输入框:
ai.ting -> 只看包含 ai.ting 的请求
!Google -> 排除包含 Google 的请求
/api/user -> 精确匹配路径
断点修改请求/响应(Breakpoints):
右键目标请求 ->Breakpoints -> 重新触发请求
-> 弹窗显示 Request 内容 -> 可修改参数 → Execute
-> 弹窗显示 Response 内容 -> 可修改返回值 → Execute
适用场景:测试服务端返回异常数据时APP会不会崩
Map Local(本地文件替换返回):
右键请求 ->Map Local → 选择一个本地的 JSON 文件
-> 此后该请求的返回值全部被替换为本地文件内容
适用场景:服务端还没开发好接口时,用 Mock 数据验证前端处理。
Rewrite(自动修改请求/响应):
Tools ->Rewrite -> Add ->设置规则
例如:
Type: Body, Where: Response,
Replace: "isVip":false → "isVip":true
作用:测试VIP和非VIP用户看到的内容差异,不需要切换账号
2.3 弱网模拟配置
Charles → Proxy → Throttle Settings → 勾选 Enable Throttling
预设场景可以直接选:
56 kbps Modem(模拟2G)
- 256 kbps ISDN(模拟3G)
512 kbps DSL(模拟弱3G)
2 Mbps ADSL(模拟4G弱信号)
也可以自定义(常用配置):
| 场景 | 下行 | 上行 | 延迟 | 丢包 | Charles配置 |
|---|---|---|---|---|---|
| 2G极限 | 50 | 20 | 500 | 10% | 选 Modem 预设 |
| 弱3G | 384 | 128 | 200 | 3% | 接近 ISDN 预设 |
| 弱4G/地铁 | 5000 | 1000 | 100 | 2% | 自行调整 |
| 网络抖动 | 20000 | 5000 | 200-1000 | 5% | 勾选 Random |
执行弱网测试的步骤:
1. 手机连接 Charles 代理
2. Charles → Throttle Settings → 选择或自定义一个网络配置
3. 在手机APP上执行目标操作
4. 观察:
是否出现 Loading 状态
超时时间是否合理
超时后提示文案是否可理解
网络恢复后能否自动继续
5. 切换不同网络配置,逐档记录表现
6. 关闭 Throttle → 恢复正常
弱网测试报告模板:
| 测试场景 | 网络配置 | 操作 | 实际表现 | 是否通过 | 备注 |
|---|---|---|---|---|---|
| 首页加载 | 2G | 冷启动 | 白屏20秒后提示"网络加载失败" | ⚠️ | 建议提前展示骨架屏 |
| 音频播放 | 弱4G | 点击播放 | 缓冲8秒后正常播放 | ✅ | --- |
| 表单提交 | 3G | 点击登录 | Loading2秒后登录成功 | ✅ | --- |
| 网络切换 | 弱4G→正常 | 播放中切网络 | 自动切换音质 | ⚠️ | 应提前缓冲 |
三、PerfDog:性能测试
PerfDog 是腾讯出品的跨平台性能测试工具,支持 Android + iOS,无需 root 也无需越狱
3.1 安装和连接
1. 下载并安装 PerfDog:https://perfdog.qq.com/
2. 手机连接到电脑(USB)→ 确认 adb devices 能看到设备
3. iOS 需要另外安装 iTunes 或 Xcode
4. 打开 PerfDog → 选择设备 → 选择要监控的APP进程
5. 点击"开始"录制
3.2 核心指标解读
| 指标 | 含义 | 参考标准 |
|---|---|---|
| FPS | 帧率,屏幕每秒刷新次数 | 滑列表 > 55,静置 = 0 |
| Jank | 卡顿次数(每10分钟) | < 10次 |
| CPU | CPU占用百分比 | 空闲 < 5%,操作 < 30% |
| Memory | 内存占用 | 视APP类型 |
| Network | 上行/下行流量速度 | --- |
| Battery | 电流消耗(mA) | 后台 < 50mA,前台 < 300mA |
| Temperature | 电池温度 | < 40℃ |
3.3 实战:检测内存泄漏
操作步骤:
1. PerfDog 开始录制
2. 启动APP → 静置30秒 → 记录基线内存(假设200MB)
3. 反复执行目标操作(如打开关闭详情页)20次
4. 回到首页 → 手动触发GC(Android: adb shell dumpsys meminfo 包名)
5. 观察最终内存值是否为基线附近
判定:
210MB -> 正常波动,无泄漏
260MB(+30%)-> 疑似泄漏,需进一步确认
350MB(+75%)-> 明显泄漏,提单
3.4 标签功能(打点记录关键操作)
PerfDog 支持在录制过程中打标签,方便事后分析"某个操作时性能如何":
执行某个操作前 → PerfDog界面点"添加标签" → 输入标签名(如"冷启动")
→ 事后在图表上能看到标签对应位置的性能数据
四、Appium:UI自动化
Appium 是目前使用最广泛的移动端 UI 自动化框架,一套代码同时支持 Android 和 iOS。
4.1 环境搭建
需要的组件:
1. JDK 11+(Java开发环境)
2. Android SDK(含 adb)
3. Node.js(Appium Server 基于 Node)
4. Appium Server
5. Appium-Python-Client(Python客户端库)
6. Appium Inspector(元素定位辅助工具)
安装命令:
bash
# 安装 Appium Server
npm install -g appium
# 安装 UIAutomator2 驱动(Android)
appium driver install uiautomator2
# 安装 XCUITest 驱动(iOS)
appium driver install xcuitest
# 安装 Python 客户端
pip install Appium-Python-Client
# 安装 Appium Inspector(桌面应用,单独下载)
# 下载地址:https://github.com/appium/appium-inspector/releases
启动 Appium Server:
bash
# 命令行启动
appium
# 输出:
# [Appium] Welcome to Appium v2.x.x
# [Appium] Appium REST http interface listener started on http://0.0.0.0:4723
4.2 编写第一个测试
python
# test_app_login.py
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
# 连接设备的Desired Capabilities
desired_caps = {
# 平台信息
"platformName": "Android",
"platformVersion": "14",
"deviceName": "Pixel_7",
# APP信息
"appPackage": "com.aitingwaiyu.app",
"appActivity": ".ui.login.LoginActivity",
# 测试配置
"noReset": True, # 不清除APP数据(保留登录态)
"autoGrantPermissions": True, # 自动授予权限
"newCommandTimeout": 300, # 命令超时5分钟
"unicodeKeyboard": True, # 使用Appium输入法支持中文输入
"resetKeyboard": True # 测试结束后恢复原输入法
}
# 连接Appium Server
driver = webdriver.Remote("http://localhost:4723", desired_caps)
# 设置显式等待
wait = WebDriverWait(driver, 10)
try:
# 1. 输入手机号
phone_input = wait.until(
EC.presence_of_element_located(
(AppiumBy.ID, "com.aitingwaiyu.app:id/etPhone")
)
)
phone_input.clear()
phone_input.send_keys("13800138000")
# 2. 点击"获取验证码"
code_btn = driver.find_element(
AppiumBy.ID, "com.aitingwaiyu.app:id/btnGetCode"
)
code_btn.click()
# 3. 等待并输入验证码(模拟环境固定验证码)
time.sleep(2)
code_input = driver.find_element(
AppiumBy.ID, "com.aitingwaiyu.app:id/etCode"
)
code_input.send_keys("000000")
# 4. 勾选用户协议
agree_cb = driver.find_element(
AppiumBy.ID, "com.aitingwaiyu.app:id/cbAgree"
)
if not agree_cb.get_attribute("checked"):
agree_cb.click()
# 5. 点击登录
login_btn = driver.find_element(
AppiumBy.ID, "com.aitingwaiyu.app:id/btnLogin"
)
login_btn.click()
# 6. 验证登录成功------等待首页元素出现
home_element = wait.until(
EC.presence_of_element_located(
(AppiumBy.XPATH, "//android.widget.TextView[@text='首页']")
)
)
assert home_element is not None, "登录失败:未跳转到首页"
print("✓ 登录成功")
except Exception as e:
# 失败截图
driver.save_screenshot("login_error.png")
print(f"✗ 测试失败: {e}")
raise
finally:
driver.quit()
4.3 元素定位实战
Appium Inspector 定位元素------它能看到页面上所有元素的结构树和属性。
定位优先级:
| 优先级 | 定位方式 | 示例 | 场景 |
|---|---|---|---|
| 1 | accessibility id | (AppiumBy.ACCESSIBILITY_ID, "登录按钮") |
iOS/Android通用 |
| 2 | resource-id | (AppiumBy.ID, "com.xx:id/btn") |
Android专用 |
| 3 | UIAutomator 选择器 | (AppiumBy.ANDROID_UIAUTOMATOR, 'text("登录")') |
按文本找控件 |
| 4 | class name | (AppiumBy.CLASS_NAME, "android.widget.Button") |
控件唯一时可用 |
| 5 | xpath | (AppiumBy.XPATH, "//Button[@text='登录']") |
兜底,最慢 |
4.4 Page Object 模式
实际项目不会把所有代码写在一个文件里,用 Page Object 封装:
python
# pages/login_page.py
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
# 元素定位器
self.PHONE_INPUT = (AppiumBy.ID, "com.aitingwaiyu.app:id/etPhone")
self.CODE_BTN = (AppiumBy.ID, "com.aitingwaiyu.app:id/btnGetCode")
self.CODE_INPUT = (AppiumBy.ID, "com.aitingwaiyu.app:id/etCode")
self.AGREE_CB = (AppiumBy.ID, "com.aitingwaiyu.app:id/cbAgree")
self.LOGIN_BTN = (AppiumBy.ID, "com.aitingwaiyu.app:id/btnLogin")
def input_phone(self, phone):
el = self.wait.until(EC.presence_of_element_located(self.PHONE_INPUT))
el.clear()
el.send_keys(phone)
def click_get_code(self):
self.driver.find_element(*self.CODE_BTN).click()
def input_code(self, code):
self.driver.find_element(*self.CODE_INPUT).send_keys(code)
def agree_protocol(self):
cb = self.driver.find_element(*self.AGREE_CB)
if cb.get_attribute("checked") != "true":
cb.click()
def click_login(self):
self.driver.find_element(*self.LOGIN_BTN).click()
return HomePage(self.driver)
# pages/home_page.py
class HomePage:
def __init__(self, driver):
self.driver = driver
self.TITLE = (AppiumBy.XPATH, "//android.widget.TextView[@text='首页']")
def is_displayed(self):
return WebDriverWait(self.driver, 5).until(
EC.presence_of_element_located(self.TITLE)
) is not None
# tests/test_login.py
def test_login_success(driver):
login_page = LoginPage(driver)
login_page.input_phone("13800138000")
login_page.click_get_code()
login_page.input_code("000000")
login_page.agree_protocol()
home_page = login_page.click_login()
assert home_page.is_displayed(), "登录失败"
4.5 运行测试
bash
# 启动 Appium(保持运行)
appium &
# 运行测试脚本
python -m pytest tests/ -v --tb=short
# 生成 Allure 报告
python -m pytest tests/ --alluredir=./allure_results
allure serve ./allure_results
五、JMeter 后端性能压测
APP 的流畅度不仅取决于前端,后端接口的性能同样关键。用 JMeter 做接口压力测试的完整步骤:
5.1 创建测试计划
第一步:线程组
线程数:100(模拟100并发用户)
Ramp-Up时间:10秒(10秒内逐步启动)
循环次数:勾选"永远"
持续时间:300秒(5分钟)
第二步:HTTP请求默认值
协议:https
服务器IP:api.aitingwaiyu.com
端口:443
第三步:HTTP Header 管理器
Content-Type: application/json
Authorization: Bearer ${token}
第四步:登录请求 + Token 提取
方法:POST
路径:/api/user/login
Body Data: {"phone":"13800138000","code":"000000"}
变量名:token
JSON路径:$.data.token
第五步:业务请求
路径:/api/course/list
参数:page=1&size=20
自动携带 ${token}
第六步:断言
JSON路径:$.code
期望值:200
第七步:监听器
监听器 -> 聚合报告 -> 核心报表
监听器 -> 查看结果树 -> 调试用(正式压测时禁用)
监听器 -> TPS图 -> 吞吐量趋势
第八步:命令行执行
bash
# 正式压测不要用 GUI(GUI 本身消耗资源影响结果)
jmeter -n -t aitingwaiyu.jmx -l result.jtl -e -o report/
# -n 非GUI模式
# -t 测试脚本文件
# -l 原始结果文件
# -e 生成HTML报告
# -o 报告输出目录
第九步:分析结果
打开 report/index.html,重点关注:
| 指标 | 关注点 |
|---|---|
| Average (ms) | 整体 < 1000ms |
| 90% Line | 90%用户感受 |
| Throughput (TPS) | 系统处理能力峰值 |
| Error % | 必须 = 0% |
六、一个版本的测试周期
Day 1:收到提测邮件
├── 拉取测试分支 → adb install 安装测试包
├── 阅读需求文档 → 确认测试范围
└── 编写/补充测试用例
Day 2:功能与接口测试
├── 手工跑核心流程用例(在自备真机上)
├── Charles 抓包验证接口参数和返回值
├── Postman 或 JMeter 单接口验证
└── 发现的 bug 提交到 TAPD / Jira
Day 3:专项测试
├── Charles Throttle 弱网模拟(2G/3G/4G/断网/切换)
├── 中断测试(来电/短信/锁屏/低电量/后台)
├── 横竖屏切换 + 不同输入法测试
└── 安装/覆盖/升级/卸载测试
Day 4:兼容性+性能
├── PerfDog 录制性能(启动/内存/帧率/耗电)
├── 多台真机(至少华为+小米+OPPO+iPhone)跑冒烟
├── 云测平台补充10-20台兼容性验证
└── JMeter 后端接口压测
Day 5:回归+报告
├── 执行自动化脚本回归
├── 整理测试报告
├── 遗留bug评估
└── 发送测试通过/阻塞结论
七、常用命令速查总表
bash
# ---- ADB ----
adb devices # 设备列表
adb install app.apk # 安装
adb uninstall 包名 # 卸载
adb logcat | grep "ERROR" # 看错误日志
adb shell am force-stop 包名 # 杀进程
adb shell input keyevent 4 # 返回键
adb shell screencap /sdcard/s.png && adb pull /sdcard/s.png # 截图
# ---- Appium ----
appium # 启动Server
appium driver list # 查看已安装驱动
pip install Appium-Python-Client # 安装客户端
# ---- JMeter ----
jmeter -n -t test.jmx -l result.jtl -e -o report/ # 命令行执行
jmeter -g result.jtl -o report/ # 从已有结果生成报告
# ---- PerfDog ----
(GUI工具,无命令行)
# ---- Charles ----
(GUI工具)