Appium的整体流程框架
将 Appium 的整个工作机制用一个详细的流程图串联起来:
graph TD
A[Appium Client] -->|1. 发送自动化指令| B[Appium Server]
B -->|2. 解析指令| C{命令类型}
C -->|3.1 元素查找| D[UiAutomator2]
C -->|3.2 操作执行| E[事件注入]
C -->|3.3 状态获取| F[状态监控]
D -->|4.1 启动| G[Bootstrap.jar]
G -->|4.2 建立连接| H[TCP Server]
D -->|5.1 获取| I[AccessibilityService]
I -->|5.2 解析| J[UI层级树]
J -->|5.3 查找| K[目标元素]
E -->|6.1 构造事件| L[MotionEvent/KeyEvent]
L -->|6.2 注入| M[InputManager]
M -->|6.3 执行| N[Android System]
F -->|7.1 监控| O[ActivityManager]
F -->|7.2 监控| P[ViewMonitor]
F -->|7.3 监控| Q[PerformanceMonitor]
K -->|8.1 返回| R[元素信息]
N -->|8.2 返回| S[操作结果]
O -->|8.3 返回| T[状态数据]
R -->|9.1 响应| B
S -->|9.2 响应| B
T -->|9.3 响应| B
B -->|10. 返回结果| A
详细流程说明:
- 初始化阶段
- Appium Client 初始化并连接到 Appium Server
- Server 启动 Bootstrap.jar
- 建立与 UiAutomator2 的通信
- 命令处理阶段
- Client 发送自动化指令
- Server 解析指令类型
- 根据类型分发到不同处理模块
- 元素查找流程
- 通过 AccessibilityService 获取 UI 层级
- 解析层级树结构
- 根据选择器匹配目标元素
- 操作执行流程
- 构造相应的事件(触摸/按键)
- 通过 InputManager 注入事件
- 系统执行相应操作
- 状态监控流程
- ActivityManager 监控页面状态
- ViewMonitor 监控元素状态
- PerformanceMonitor 监控性能数据
- 结果返回流程
- 收集操作结果
- 获取状态数据
- 返回给 Appium Client
关键节点说明:
- 通信层
// Appium Server 与 Client 通信
WebDriverProtocol.handleRequest(Request request)
// Server 与 Bootstrap.jar 通信
BootstrapServer.handleCommand(Command command)
- 元素处理层
// 元素查找
UiAutomator.findElement(Selector selector)
// 元素操作
ElementOperation.performAction(UiElement element, Action action)
- 事件处理层
// 事件注入
EventInjector.inject(InputEvent event)
// 状态监控
StateMonitor.collectState()
- 结果处理层
// 结果封装
ResponseBuilder.build(OperationResult result)
// 结果返回
Server.sendResponse(Response response)
数据流转示例:
// 1. 点击操作的完整流程
public void clickElement(String elementId) {
// 查找元素
UiElement element = UiAutomator.findElement(elementId);
// 构造点击事件
MotionEvent event = EventBuilder.buildTouchEvent(
element.getCenter()
);
// 注入事件
InputManager.injectEvent(event);
// 等待操作完成
StateMonitor.waitForIdle();
// 返回结果
return new OperationResult(SUCCESS);
}
// 2. 获取文本的完整流程
public String getText(String elementId) {
// 查找元素
UiElement element = UiAutomator.findElement(elementId);
// 获取文本
String text = element.getText();
// 验证结果
StateMonitor.validateElementState(element);
// 返回文本
return text;
}
这个流程展示了 Appium 如何将各个组件协同工作,实现自动化测试的完整功能。每个环节都有其特定的职责,共同保证了自动化测试的可靠性和效率。
Appium的基本使用
一、Appium 是什么?
想象 Appium 是一个"机器人测试员":
- 它可以像人一样操作手机
- 能自动点击、滑动、输入文字
- 还能检查界面是否正确
- 24小时不知疲倦地工作
graph TD
A[Appium机器人] --> B[点击操作]
A --> C[滑动操作]
A --> D[输入文字]
A --> E[截图验证]
A --> F[自动化测试]
二、基本操作示例
# 假设我们在测试一个购物App
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
class ShoppingTest:
def test_buy_product(self):
# 1. 打开App(就像人点击App图标一样)
self.driver.launch_app()
# 2. 搜索商品(像人一样在搜索框输入)
search_box = self.driver.find_element(
MobileBy.ID, "search_input")
search_box.send_keys("iPhone")
# 3. 点击搜索按钮(像人用手指点击)
search_button = self.driver.find_element(
MobileBy.ID, "search_button")
search_button.click()
# 4. 选择商品(像人滑动查看并点击)
product = self.driver.find_element(
MobileBy.XPATH, "//android.widget.TextView[@text='iPhone 15']")
product.click()
# 5. 加入购物车(像人点击加入购物车)
add_cart = self.driver.find_element(
MobileBy.ID, "add_to_cart")
add_cart.click()
三、模拟真实场景
# 模拟用户真实购物流程
def test_shopping_flow(self):
# 1. 先等等广告关闭(像人要等广告)
self.wait_for_element("close_ad_button").click()
# 2. 看看有什么优惠(像人浏览优惠信息)
self.swipe_up() # 向上滑动
# 3. 挑选商品(像人仔细挑选)
products = self.find_elements("product_list")
for product in products:
price = product.find_element("price_text").text
if float(price) < 1000: # 预算1000以内
product.click()
break
# 4. 查看详情(像人查看商品详情)
self.swipe_up() # 滑动查看详情
# 5. 选择规格(像人选择喜欢的款式)
self.click("color_black") # 选择黑色
self.click("size_large") # 选择大号
四、检查功能是否正常
# 像质检员一样检查功能
def test_app_functions(self):
# 1. 检查登录功能
def check_login():
self.input_text("username", "test123")
self.input_text("password", "123456")
self.click("login_button")
# 检查是否登录成功
assert self.is_element_present("user_avatar")
# 2. 检查购物车功能
def check_cart():
self.click("cart_icon")
# 确认购物车页面已打开
assert self.get_text("page_title") == "购物车"
# 检查商品数量
items = self.find_elements("cart_items")
assert len(items) > 0
# 3. 检查支付功能
def check_payment():
self.click("checkout_button")
# 选择支付方式
self.click("alipay_option")
# 确认跳转到支付页面
assert self.is_element_present("pay_qrcode")
五、处理各种意外情况
# 像经验丰富的测试员一样处理意外
class ExceptionHandler:
def handle_popup(self):
try:
# 1. 处理更新提示
if self.is_element_present("update_popup"):
self.click("cancel_update")
# 2. 处理广告弹窗
if self.is_element_present("ad_popup"):
self.click("close_ad")
# 3. 处理网络错误
if self.is_element_present("network_error"):
self.click("retry_button")
except Exception as e:
print(f"遇到意外情况:{e}")
self.take_screenshot("error.png")
六、生成测试报告
# 像写测试总结一样生成报告
class TestReport:
def generate_report(self):
report = {
"测试概况": {
"总用例数": 100,
"通过数": 95,
"失败数": 5
},
"测试详情": [
{
"用例": "登录功能",
"结果": "通过",
"耗时": "2.5秒"
},
{
"用例": "购物流程",
"结果": "通过",
"耗时": "15秒"
}
],
"问题记录": [
"支付页面偶尔加载超时",
"商品列表滑动不流畅"
]
}
return report
七、实用的测试小技巧
class TestingTips:
# 1. 等待元素出现(像人要有耐心)
def wait_for_element(self, element_id, timeout=10):
return WebDriverWait(self.driver, timeout).until(
EC.presence_of_element_located((MobileBy.ID, element_id))
)
# 2. 智能滑动(像人一样智能判断)
def smart_swipe(self):
screen_size = self.driver.get_window_size()
start_y = screen_size['height'] * 0.8
end_y = screen_size['height'] * 0.2
while not self.is_element_present("target_element"):
self.driver.swipe(
start_x=screen_size['width']/2,
start_y=start_y,
end_x=screen_size['width']/2,
end_y=end_y,
duration=800
)
# 3. 记录关键步骤(像写日记一样)
def log_step(self, step_name):
print(f"执行步骤:{step_name}")
self.take_screenshot(f"{step_name}.png")
八、最佳实践建议
- 像写剧本一样写测试用例:
- 场景要完整
- 步骤要清晰
- 预期要明确
- 像导演一样设计测试流程:
- 考虑各种场景
- 处理各种意外
- 合理安排顺序
- 像演员一样执行测试:
- 按步骤执行
- 注意细节
- 记录问题
- 像观众一样验证结果:
- 检查是否符合预期
- 关注用户体验
- 提出改进建议
九、使用建议
- 新手入门:
- 从简单场景开始
- 多看官方文档
- 参考示例代码
- 进阶使用:
- 设计测试框架
- 优化测试代码
- 提高测试效率
- 高级应用:
- 持续集成
- 性能测试
- 自动化报告
通过这样的方式,Appium 就像一个不知疲倦的测试员,帮我们完成大量重复的测试工作,提高测试效率和质量!
Appium原理详解
一、整体架构
graph TD
A[Appium Client] --> B[Appium Server]
B --> C[UiAutomator2/XCUITest]
C --> D[Native App]
B --> E[Bootstrap.jar]
E --> F[Android/iOS System]
二、核心通信机制
public class CommunicationBridge {
// 1. WebDriver协议通信
public class AppiumServer {
public void handleRequest(Request request) {
// REST API 请求处理
switch (request.getCommand()) {
case "findElement":
// 转发给 UiAutomator2
return uiautomator.findElement(request.getSelector());
case "click":
// 执行点击操作
return uiautomator.click(request.getElementId());
case "getText":
// 获取文本内容
return uiautomator.getText(request.getElementId());
}
}
}
// 2. Bootstrap.jar 通信
public class BootstrapServer {
private final ServerSocket server;
public void startServer() {
// 启动 TCP 服务器
server = new ServerSocket(8888);
// 等待 UiAutomator 连接
handleConnection(server.accept());
}
}
}
三、UI 元素获取机制
public class ElementFinder {
// 1. AccessibilityService 方式
public class UiAutomatorBridge {
public UiElement findElement(Selector selector) {
// 获取当前窗口的 Accessibility 层级
AccessibilityNodeInfo root =
UiAutomator.getInstance().getRootInActiveWindow();
// 根据选择器查找元素
switch (selector.getType()) {
case ID:
return findById(root, selector.getValue());
case XPATH:
return findByXPath(root, selector.getValue());
case CLASS_NAME:
return findByClassName(root, selector.getValue());
}
}
private UiElement findById(AccessibilityNodeInfo root, String id) {
// 遍历 Accessibility 树
List<AccessibilityNodeInfo> nodes =
root.findAccessibilityNodeInfosByViewId(id);
return convertToUiElement(nodes.get(0));
}
}
}
四、元素操作实现
public class ElementOperation {
// 1. 点击操作
public void click(UiElement element) {
// 获取元素中心点坐标
Point center = element.getCenter();
// 构造点击事件
MotionEvent downEvent = MotionEvent.obtain(
SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(),
MotionEvent.ACTION_DOWN,
center.x,
center.y,
0
);
// 注入事件
injectMotionEvent(downEvent);
}
// 2. 输入文本
public void sendKeys(UiElement element, String text) {
// 确保元素可编辑
if (element.isEditable()) {
// 通过 InputConnection 输入文本
InputConnection ic = element.getInputConnection();
ic.commitText(text, 1);
}
}
}
五、状态监控实现
public class StateMonitor {
// 1. Activity 监控
public class ActivityMonitor {
public String getCurrentActivity() {
// 通过 ActivityManager 获取当前 Activity
ActivityManager am = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1)
.get(0).topActivity;
return cn.getClassName();
}
}
// 2. 界面状态监控
public class ViewMonitor {
public boolean waitForElement(Selector selector, long timeout) {
long endTime = System.currentTimeMillis() + timeout;
while (System.currentTimeMillis() < endTime) {
if (findElement(selector) != null) {
return true;
}
SystemClock.sleep(500);
}
return false;
}
}
}
六、事件注入机制
public class EventInjector {
// 1. Touch 事件注入
public void injectTouchEvent(MotionEvent event) {
// 通过 InputManager 注入事件
InputManager.getInstance().injectInputEvent(
event,
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
);
}
// 2. Key 事件注入
public void injectKeyEvent(KeyEvent event) {
// 通过 InputManager 注入按键事件
InputManager.getInstance().injectInputEvent(
event,
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
);
}
}
七、性能数据采集
public class PerformanceCollector {
// 1. CPU 使用率
public float getCpuUsage(String packageName) {
// 读取 /proc/stat 获取 CPU 信息
return calculateCpuUsage(readProcStat());
}
// 2. 内存使用
public long getMemoryInfo(String packageName) {
ActivityManager am = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
// 获取进程内存信息
Debug.MemoryInfo[] memInfo = am.getProcessMemoryInfo(
new int[]{getPid(packageName)});
return memInfo[0].getTotalPss();
}
}
八、原理总结
- 核心实现机制:
- 基于 Android Instrumentation
- 使用 Accessibility 服务
- 通过事件注入实现操作
- 元素定位原理:
- 解析 UI 层级树
- 匹配选择器条件
- 返回元素信息
- 操作实现原理:
- 构造 MotionEvent
- 通过 InputManager 注入
- 监听操作结果
- 状态获取原理:
- 使用 ActivityManager
- 监听 Accessibility 事件
- 分析界面层级
九、技术要点
public class TechnicalPoints {
// 1. UI 层级获取
private AccessibilityNodeInfo getUiHierarchy() {
// 通过 UiAutomator 获取
return UiAutomator.getInstance().getRootInActiveWindow();
}
// 2. 事件构造
private MotionEvent createTouchEvent(Point point) {
return MotionEvent.obtain(
SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(),
MotionEvent.ACTION_DOWN,
point.x,
point.y,
0
);
}
// 3. 元素属性获取
private Map<String, String> getElementAttributes(
AccessibilityNodeInfo node) {
Map<String, String> attributes = new HashMap<>();
attributes.put("text", node.getText().toString());
attributes.put("className", node.getClassName().toString());
attributes.put("packageName", node.getPackageName().toString());
return attributes;
}
}
总结:
- Appium 通过多层架构实现自动化
- 底层依赖系统 API 和服务
- 使用事件注入模拟用户操作
- 通过 Accessibility 获取界面信息
- 实现了完整的监控和控制机制