提交UI自动化插件
This commit is contained in:
@@ -0,0 +1,760 @@
|
||||
# UI 自动化插件设计方案
|
||||
|
||||
> 插件名称建议:手机 UI 自动化
|
||||
> 插件 ID 建议:`mobile-ui-auto`
|
||||
> 核心技术栈:JavaFX 插件壳 + Python Runner + Airtest + Poco(pocoui) + ADB
|
||||
> 目标:在 WorkUtils 中提供一个可视化入口,用于连接 Android 手机,对指定 APP 执行自动点击、输入、滑动、断言、截图和流程测试。
|
||||
|
||||
## 1. 设计结论
|
||||
|
||||
本插件不建议在 Java 里直接实现手机 UI 自动化细节,而是采用“双层架构”:
|
||||
|
||||
```text
|
||||
WorkUtils Java 插件层
|
||||
负责:可视化配置、脚本管理、流程编排、进度展示、报告入口、进程控制
|
||||
|
||||
Python 自动化执行层
|
||||
负责:连接设备、启动 APP、查找元素/图片、点击输入、断言、截图、生成运行结果
|
||||
```
|
||||
|
||||
选择理由:
|
||||
|
||||
- `pocoui/Poco` 和 `Airtest` 原生就是 Python 生态,直接用 Python 写动作最顺。
|
||||
- Java 项目已有插件框架,适合做工具入口、配置管理、运行控制和报告汇总。
|
||||
- 手机自动化经常依赖 ADB、截图、图片模板、设备状态,这些动作放在独立 Python 进程中更容易隔离异常。
|
||||
- 后续如果要加 `uiautomator2`、Appium 或云真机,可以扩展 Python Runner,不破坏 Java 插件壳。
|
||||
|
||||
## 2. 能力边界
|
||||
|
||||
### 2.1 首期能力
|
||||
|
||||
| 能力 | 说明 |
|
||||
|------|------|
|
||||
| 设备检测 | 通过 ADB 列出已连接设备,展示 serial、状态、系统版本 |
|
||||
| APP 启动 | 输入 package/activity 或仅 package,启动被测 APP |
|
||||
| 自动点击 | 支持元素定位点击、图片定位点击、坐标点击 |
|
||||
| 自动输入 | 支持输入文本、清空输入框、粘贴式输入 |
|
||||
| 手势模拟 | 支持滑动、返回、Home、等待、按键事件 |
|
||||
| 元素定位 | 优先 Poco 元素定位,必要时使用图片识别兜底 |
|
||||
| 流程脚本 | 支持 YAML/JSON 步骤流,自动转 Python 执行 |
|
||||
| Python 脚本 | 支持直接运行 `.py` 或 `.air` 脚本 |
|
||||
| 运行日志 | Java UI 实时显示 Python stdout/stderr |
|
||||
| 截图报告 | 每个关键步骤可保存截图,失败时自动截图 |
|
||||
| 断言 | 支持元素存在、图片存在、文本存在、页面等待 |
|
||||
|
||||
### 2.2 非首期能力
|
||||
|
||||
| 能力 | 放到后续原因 |
|
||||
|------|--------------|
|
||||
| iOS 自动化 | 依赖环境复杂,首期聚焦 Android |
|
||||
| 分布式设备池 | 需要设备调度、队列和并发报告模型 |
|
||||
| 录制回放 | 可以实现,建议第二阶段加入;首期先保证脚本执行闭环 |
|
||||
| 智能探索测试 | 需要页面状态建模和策略引擎 |
|
||||
| Appium 兼容 | 跨平台价值高,但启动成本比 Poco 高 |
|
||||
|
||||
## 3. 推荐技术组合
|
||||
|
||||
### 3.1 主链路
|
||||
|
||||
```text
|
||||
Python + Airtest + Poco(pocoui) + ADB
|
||||
```
|
||||
|
||||
- Airtest:设备连接、截图、图像识别、触控、断言、报告。
|
||||
- Poco(pocoui):UI 树定位、元素点击、输入、等待。
|
||||
- ADB:设备列表、安装 APK、启动 APP、logcat、底层命令。
|
||||
- AirtestIDE:作为辅助工具,用于新手录制/定位图片/生成初版脚本。
|
||||
|
||||
### 3.2 兜底链路
|
||||
|
||||
```text
|
||||
uiautomator2
|
||||
```
|
||||
|
||||
首期只做预留,不默认引入。适合 Poco 在部分原生控件上拿不到稳定 UI 层级时,作为元素定位增强。
|
||||
|
||||
## 4. 总体架构
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["MainWindow / PluginManager"] --> B["MobileUiAutomationPlugin"]
|
||||
B --> C["MobileUiAutomationWindow"]
|
||||
B --> D["MobileAutomationService"]
|
||||
D --> E["PythonEnvManager"]
|
||||
D --> F["DeviceService"]
|
||||
D --> G["CaseService"]
|
||||
D --> H["PythonRunner"]
|
||||
H --> I["runner.py"]
|
||||
I --> J["Airtest API"]
|
||||
I --> K["Poco AndroidUiautomationPoco"]
|
||||
I --> L["ADB"]
|
||||
J --> M["Android Device"]
|
||||
K --> M
|
||||
L --> M
|
||||
I --> N["reports / screenshots / result.json"]
|
||||
H --> O["实时日志回调"]
|
||||
O --> C
|
||||
```
|
||||
|
||||
## 5. Java 插件层设计
|
||||
|
||||
### 5.1 包结构
|
||||
|
||||
```text
|
||||
src/main/java/com/example/workutils/biz/mobileuiauto/
|
||||
MobileUiAutomationPlugin.java
|
||||
MobileUiAutomationWindow.java
|
||||
service/
|
||||
MobileAutomationService.java
|
||||
PythonEnvManager.java
|
||||
DeviceService.java
|
||||
CaseService.java
|
||||
PythonRunner.java
|
||||
model/
|
||||
DeviceInfo.java
|
||||
AutomationCase.java
|
||||
AutomationRunConfig.java
|
||||
AutomationRunResult.java
|
||||
AutomationStep.java
|
||||
```
|
||||
|
||||
### 5.2 资源和脚本结构
|
||||
|
||||
```text
|
||||
src/main/resources/mobile-ui-auto/
|
||||
python/
|
||||
runner.py
|
||||
requirements.txt
|
||||
flow_executor.py
|
||||
action_library.py
|
||||
templates/
|
||||
sample-login-flow.yaml
|
||||
sample-case.py
|
||||
```
|
||||
|
||||
运行时目录:
|
||||
|
||||
```text
|
||||
data/mobile-ui-auto/
|
||||
config.json
|
||||
cases/
|
||||
login-flow.yaml
|
||||
smoke-test.py
|
||||
images/
|
||||
login_button.png
|
||||
home_tab.png
|
||||
reports/
|
||||
20260703-153000/
|
||||
result.json
|
||||
run.log
|
||||
screenshots/
|
||||
airtest-report/
|
||||
```
|
||||
|
||||
### 5.3 插件描述
|
||||
|
||||
```java
|
||||
private static final PluginDescriptor DESCRIPTOR = PluginDescriptor.builder()
|
||||
.id("mobile-ui-auto")
|
||||
.name("手机UI自动化")
|
||||
.version("1.0.0")
|
||||
.description("基于 Python + Airtest + Poco + ADB 的手机 APP 流程自动化测试工具")
|
||||
.order(80)
|
||||
.icon("📱")
|
||||
.build();
|
||||
```
|
||||
|
||||
项目里部分中文注释目前存在编码显示异常,实际落地时建议统一以 UTF-8 保存源码和文档,避免 UI 文案乱码。
|
||||
|
||||
## 6. JavaFX 界面设计
|
||||
|
||||
插件采用嵌入式 JavaFX 面板,分为 5 个区域:
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ 顶部工具栏:设备刷新 | Python环境检查 | 打开用例目录 | 打开报告目录 │
|
||||
├───────────────┬──────────────────────────────────────────────┤
|
||||
│ 左侧设备区 │ 右侧执行区 │
|
||||
│ - 设备列表 │ - 被测APP package/activity │
|
||||
│ - 当前设备 │ - 用例选择 YAML/PY/.air │
|
||||
│ - 连接状态 │ - 运行参数:超时、失败截图、报告开关 │
|
||||
├───────────────┼──────────────────────────────────────────────┤
|
||||
│ 用例列表 │ 步骤预览 / 脚本预览 │
|
||||
├───────────────┴──────────────────────────────────────────────┤
|
||||
│ 底部日志区:实时日志、进度、失败原因、报告链接 │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 6.1 页面控件
|
||||
|
||||
| 区域 | 控件 |
|
||||
|------|------|
|
||||
| 设备区 | `ComboBox<DeviceInfo>`、刷新按钮、设备状态标签 |
|
||||
| APP 配置 | package 输入框、activity 输入框、启动 APP 按钮 |
|
||||
| 用例区 | 用例表格、新建/导入/删除/复制按钮 |
|
||||
| 执行区 | 运行、停止、保存配置、打开报告按钮 |
|
||||
| 日志区 | `TextArea` 实时追加日志,错误行高亮 |
|
||||
|
||||
### 6.2 用户流程
|
||||
|
||||
```text
|
||||
1. 打开“手机UI自动化”插件
|
||||
2. 点击“检查环境”
|
||||
3. 连接手机并打开 USB 调试
|
||||
4. 点击“刷新设备”
|
||||
5. 选择设备
|
||||
6. 填写或选择 APP package
|
||||
7. 选择流程用例
|
||||
8. 点击“运行”
|
||||
9. 查看实时日志、截图、结果
|
||||
10. 失败时打开报告定位问题
|
||||
```
|
||||
|
||||
## 7. Python 执行层设计
|
||||
|
||||
### 7.1 requirements.txt
|
||||
|
||||
```text
|
||||
airtest
|
||||
pocoui
|
||||
pyyaml
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- `airtest` 提供设备连接、点击、滑动、截图、图片断言、报告能力。
|
||||
- `pocoui` 提供 Poco UI 层级定位能力。
|
||||
- `pyyaml` 用于读取流程式 YAML 用例。
|
||||
|
||||
### 7.2 runner.py 入参
|
||||
|
||||
Java 通过 `ProcessBuilder` 调用 Python:
|
||||
|
||||
```text
|
||||
python runner.py
|
||||
--device Android:///<serial>
|
||||
--package com.xxx.app
|
||||
--activity .MainActivity
|
||||
--case data/mobile-ui-auto/cases/login-flow.yaml
|
||||
--report data/mobile-ui-auto/reports/20260703-153000
|
||||
--timeout 20
|
||||
```
|
||||
|
||||
### 7.3 runner.py 输出协议
|
||||
|
||||
Python 进程按行输出 JSON,Java 端解析后更新 UI:
|
||||
|
||||
```json
|
||||
{"type":"log","level":"INFO","message":"连接设备成功"}
|
||||
{"type":"step_start","index":1,"name":"点击登录按钮"}
|
||||
{"type":"screenshot","path":"reports/xxx/screenshots/001.png"}
|
||||
{"type":"step_pass","index":1,"elapsedMs":532}
|
||||
{"type":"step_fail","index":2,"message":"元素不存在: username"}
|
||||
{"type":"done","success":false,"reportPath":"reports/xxx/result.json"}
|
||||
```
|
||||
|
||||
如果输出不是 JSON,则按普通日志展示,避免第三方库日志破坏执行。
|
||||
|
||||
## 8. 流程用例 DSL 设计
|
||||
|
||||
首期建议用 YAML 描述流程,便于非 Python 用户维护。
|
||||
|
||||
### 8.1 示例:登录流程
|
||||
|
||||
```yaml
|
||||
name: login-smoke
|
||||
app:
|
||||
package: com.example.app
|
||||
activity: .MainActivity
|
||||
device:
|
||||
connect: Android:///
|
||||
settings:
|
||||
timeout: 20
|
||||
screenshotOnFail: true
|
||||
steps:
|
||||
- name: 等待首页
|
||||
action: wait
|
||||
target:
|
||||
type: poco
|
||||
query:
|
||||
text: 首页
|
||||
timeout: 15
|
||||
|
||||
- name: 点击我的
|
||||
action: tap
|
||||
target:
|
||||
type: poco
|
||||
query:
|
||||
text: 我的
|
||||
|
||||
- name: 输入用户名
|
||||
action: input
|
||||
target:
|
||||
type: poco
|
||||
query:
|
||||
name: username_input
|
||||
text: test_user
|
||||
|
||||
- name: 输入密码
|
||||
action: input
|
||||
target:
|
||||
type: poco
|
||||
query:
|
||||
name: password_input
|
||||
text: "123456"
|
||||
|
||||
- name: 点击登录
|
||||
action: tap
|
||||
target:
|
||||
type: image
|
||||
path: images/login_button.png
|
||||
|
||||
- name: 断言登录成功
|
||||
action: assert_exists
|
||||
target:
|
||||
type: poco
|
||||
query:
|
||||
text: 退出登录
|
||||
```
|
||||
|
||||
### 8.2 支持动作
|
||||
|
||||
| action | 说明 | target |
|
||||
|--------|------|--------|
|
||||
| `start_app` | 启动 APP | package/activity |
|
||||
| `stop_app` | 停止 APP | package |
|
||||
| `tap` | 点击 | poco/image/coord |
|
||||
| `double_tap` | 双击 | poco/image/coord |
|
||||
| `input` | 输入文本 | poco |
|
||||
| `clear` | 清空输入框 | poco |
|
||||
| `swipe` | 滑动 | coord/image |
|
||||
| `wait` | 等待元素出现 | poco/image |
|
||||
| `sleep` | 固定等待 | seconds |
|
||||
| `keyevent` | Android 按键 | BACK/HOME/ENTER |
|
||||
| `screenshot` | 截图 | path |
|
||||
| `assert_exists` | 断言存在 | poco/image |
|
||||
| `assert_not_exists` | 断言不存在 | poco/image |
|
||||
|
||||
### 8.3 定位方式
|
||||
|
||||
```yaml
|
||||
target:
|
||||
type: poco
|
||||
query:
|
||||
text: 登录
|
||||
```
|
||||
|
||||
```yaml
|
||||
target:
|
||||
type: poco
|
||||
query:
|
||||
name: com.example:id/login
|
||||
```
|
||||
|
||||
```yaml
|
||||
target:
|
||||
type: image
|
||||
path: images/login_button.png
|
||||
threshold: 0.85
|
||||
```
|
||||
|
||||
```yaml
|
||||
target:
|
||||
type: coord
|
||||
x: 540
|
||||
y: 1600
|
||||
```
|
||||
|
||||
推荐优先级:
|
||||
|
||||
```text
|
||||
poco 元素定位 > 图片定位 > 坐标定位
|
||||
```
|
||||
|
||||
坐标定位只做兜底,因为不同分辨率、字体缩放和设备比例会导致脚本脆弱。
|
||||
|
||||
## 9. 录制回放设计
|
||||
|
||||
可以实现“手动模拟点击后记录下来,后续按记录自动模拟”的能力。推荐把它设计成“录制器 + 定位增强 + YAML 生成 + 回放校验”,而不是只保存点击坐标。
|
||||
|
||||
### 9.1 录制目标
|
||||
|
||||
用户在手机上手动操作一遍 APP,插件自动记录:
|
||||
|
||||
- 点击、长按、滑动、返回、Home、输入文本等事件。
|
||||
- 每一步操作前后的截图。
|
||||
- 当前页面 UI 层级信息。
|
||||
- 被点击元素的 Poco 定位信息。
|
||||
- 坐标和屏幕分辨率,作为兜底。
|
||||
- 步骤间等待时间。
|
||||
|
||||
录制结束后生成一份可编辑 YAML 用例,后续可直接运行。
|
||||
|
||||
### 9.2 为什么不能只记录坐标
|
||||
|
||||
只记录坐标虽然最容易实现,但稳定性最差:
|
||||
|
||||
- 不同手机分辨率、状态栏高度、导航栏模式会导致坐标偏移。
|
||||
- APP 页面布局变化后,坐标仍然能点下去,但可能点错位置。
|
||||
- 弹窗、键盘、网络慢加载会让固定坐标回放失败。
|
||||
|
||||
因此每个录制步骤建议保存“多定位策略”:
|
||||
|
||||
```yaml
|
||||
- name: 点击登录按钮
|
||||
action: tap
|
||||
target:
|
||||
preferred: poco
|
||||
poco:
|
||||
text: 登录
|
||||
name: com.example:id/login_button
|
||||
image:
|
||||
path: images/step_003_login_button.png
|
||||
threshold: 0.85
|
||||
coord:
|
||||
x: 524
|
||||
y: 1680
|
||||
screenWidth: 1080
|
||||
screenHeight: 2400
|
||||
waitAfterMs: 500
|
||||
```
|
||||
|
||||
回放时优先使用 Poco 元素定位;找不到元素时用图片识别;图片也找不到时才按比例坐标点击。
|
||||
|
||||
### 9.3 录制实现方案
|
||||
|
||||
首选实现路径:
|
||||
|
||||
```text
|
||||
Java UI 点击“开始录制”
|
||||
-> Python recorder.py 连接 Android 设备
|
||||
-> 周期性读取屏幕截图和 UI 树
|
||||
-> 用户在真实手机上操作
|
||||
-> recorder 捕获操作差异/辅助确认
|
||||
-> 生成 recorded-flow.yaml
|
||||
```
|
||||
|
||||
Android 上直接无侵入捕获所有触摸事件有权限限制,因此录制有两种可落地模式:
|
||||
|
||||
| 模式 | 实现方式 | 优点 | 缺点 |
|
||||
|------|----------|------|------|
|
||||
| 辅助录制模式 | Java/Python 显示手机镜像或截图,用户在插件界面点,插件记录点击 | 最容易落地,可准确记录点击坐标和截图 | 不是直接点手机屏幕 |
|
||||
| 真机旁路录制 | 用户点真机,录制器通过截图/UI树变化推断步骤,必要时让用户确认 | 更贴近真实操作 | 自动推断难度更高 |
|
||||
|
||||
建议 MVP 采用“辅助录制模式”:插件显示手机当前截图,用户点击截图上的位置,插件把点击转发到手机,并同时记录步骤。这样既能完成真实操作,又能准确生成脚本。
|
||||
|
||||
### 9.4 辅助录制交互
|
||||
|
||||
```text
|
||||
1. 用户选择设备和 APP
|
||||
2. 点击“开始录制”
|
||||
3. 插件实时刷新手机截图
|
||||
4. 用户在截图上点击、滑动、输入
|
||||
5. 插件执行对应手机动作
|
||||
6. 插件记录动作、截图、UI树和定位候选
|
||||
7. 用户点击“停止录制”
|
||||
8. 插件生成 YAML 用例
|
||||
9. 用户点击“试运行”校验录制流程
|
||||
```
|
||||
|
||||
### 9.5 录制事件模型
|
||||
|
||||
```json
|
||||
{
|
||||
"index": 3,
|
||||
"action": "tap",
|
||||
"timestamp": 1783071000123,
|
||||
"screen": {
|
||||
"width": 1080,
|
||||
"height": 2400
|
||||
},
|
||||
"point": {
|
||||
"x": 524,
|
||||
"y": 1680
|
||||
},
|
||||
"pocoCandidates": [
|
||||
{
|
||||
"text": "登录",
|
||||
"name": "com.example:id/login_button",
|
||||
"bounds": [420, 1600, 660, 1740]
|
||||
}
|
||||
],
|
||||
"imageTemplate": "images/step_003_login_button.png",
|
||||
"beforeScreenshot": "screenshots/003_before.png",
|
||||
"afterScreenshot": "screenshots/003_after.png",
|
||||
"waitAfterMs": 500
|
||||
}
|
||||
```
|
||||
|
||||
### 9.6 Python 录制器能力
|
||||
|
||||
`recorder.py` 需要提供:
|
||||
|
||||
- 获取当前截图。
|
||||
- 获取当前 Poco UI 树。
|
||||
- 接收 Java UI 传来的点击/滑动/输入事件。
|
||||
- 把屏幕坐标转换为设备坐标。
|
||||
- 执行动作并记录前后截图。
|
||||
- 根据点击位置匹配最近的 Poco 元素。
|
||||
- 裁剪点击区域图片,生成图片模板。
|
||||
- 输出 `recorded-flow.yaml`。
|
||||
|
||||
### 9.7 回放策略
|
||||
|
||||
回放每一步时按以下顺序执行:
|
||||
|
||||
```text
|
||||
1. 如果有 Poco 定位,先用 Poco 查找并点击
|
||||
2. 如果 Poco 失败,使用图片模板识别并点击
|
||||
3. 如果图片失败,使用按分辨率比例换算后的坐标点击
|
||||
4. 每步后等待 UI 稳定,必要时对比 afterScreenshot 或断言元素
|
||||
5. 任一步失败时截图并写入报告
|
||||
```
|
||||
|
||||
### 9.8 录制回放验收标准
|
||||
|
||||
- 能在插件界面显示当前手机截图。
|
||||
- 用户点击截图后,手机同步执行点击。
|
||||
- 插件能记录点击步骤并生成 YAML。
|
||||
- YAML 能再次运行并完成同样流程。
|
||||
- 至少支持点击、输入、滑动、返回。
|
||||
- 失败时能定位到具体步骤并展示截图。
|
||||
|
||||
## 10. Python 核心伪代码
|
||||
|
||||
```python
|
||||
from airtest.core.api import *
|
||||
from poco.drivers.android.uiautomation import AndroidUiautomationPoco
|
||||
|
||||
def connect(device_uri):
|
||||
auto_setup(__file__, devices=[device_uri])
|
||||
return AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)
|
||||
|
||||
def tap_target(poco, target):
|
||||
if target["type"] == "poco":
|
||||
node = find_poco(poco, target["query"])
|
||||
node.wait_for_appearance(timeout=target.get("timeout", 10))
|
||||
node.click()
|
||||
elif target["type"] == "image":
|
||||
touch(Template(target["path"], threshold=target.get("threshold", 0.85)))
|
||||
elif target["type"] == "coord":
|
||||
touch((target["x"], target["y"]))
|
||||
|
||||
def input_target(poco, target, text):
|
||||
node = find_poco(poco, target["query"])
|
||||
node.wait_for_appearance(timeout=target.get("timeout", 10))
|
||||
node.click()
|
||||
text(text)
|
||||
```
|
||||
|
||||
注意:实际实现里要避免函数名 `text` 与 Airtest API 冲突,可封装为 `airtest_text` 或显式 import。
|
||||
|
||||
## 11. 环境管理设计
|
||||
|
||||
### 11.1 Java 检查项
|
||||
|
||||
| 检查项 | 命令 | 失败提示 |
|
||||
|--------|------|----------|
|
||||
| Python | `python --version` | 未安装 Python 或未加入 PATH |
|
||||
| pip | `python -m pip --version` | pip 不可用 |
|
||||
| airtest | `python -c "import airtest"` | 请安装 airtest |
|
||||
| pocoui | `python -c "import poco"` | 请安装 pocoui |
|
||||
| adb | `adb devices` | ADB 不可用或设备未授权 |
|
||||
|
||||
### 11.2 自动安装策略
|
||||
|
||||
首期建议提供“复制安装命令”,不自动安装:
|
||||
|
||||
```text
|
||||
python -m pip install -U airtest pocoui pyyaml
|
||||
```
|
||||
|
||||
原因:
|
||||
|
||||
- 公司内网、代理、镜像源差异大。
|
||||
- 自动安装可能污染用户全局 Python。
|
||||
- 后续可以加虚拟环境模式:`data/mobile-ui-auto/.venv`。
|
||||
|
||||
## 12. 报告设计
|
||||
|
||||
每次执行生成独立目录:
|
||||
|
||||
```text
|
||||
reports/20260703-153000/
|
||||
result.json
|
||||
run.log
|
||||
screenshots/
|
||||
001-start.png
|
||||
002-login-fail.png
|
||||
summary.html
|
||||
```
|
||||
|
||||
`result.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"caseName": "login-smoke",
|
||||
"device": "Android:///xxx",
|
||||
"package": "com.example.app",
|
||||
"success": false,
|
||||
"startTime": "2026-07-03T15:30:00+08:00",
|
||||
"endTime": "2026-07-03T15:30:18+08:00",
|
||||
"steps": [
|
||||
{
|
||||
"index": 1,
|
||||
"name": "等待首页",
|
||||
"status": "PASS",
|
||||
"elapsedMs": 1200
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"name": "点击登录",
|
||||
"status": "FAIL",
|
||||
"message": "图片不存在: images/login_button.png",
|
||||
"screenshot": "screenshots/002-login-fail.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 13. Java 服务接口草案
|
||||
|
||||
```java
|
||||
public interface MobileAutomationService {
|
||||
EnvironmentCheckResult checkEnvironment();
|
||||
|
||||
List<DeviceInfo> listDevices();
|
||||
|
||||
List<AutomationCase> listCases();
|
||||
|
||||
AutomationRunHandle runCase(AutomationRunConfig config, AutomationCallback callback);
|
||||
|
||||
void stopRun(String runId);
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
public interface AutomationCallback {
|
||||
void onLog(String level, String message);
|
||||
|
||||
void onStepStart(int index, String name);
|
||||
|
||||
void onStepPass(int index, long elapsedMs);
|
||||
|
||||
void onStepFail(int index, String message, String screenshotPath);
|
||||
|
||||
void onDone(AutomationRunResult result);
|
||||
}
|
||||
```
|
||||
|
||||
## 14. 进程控制设计
|
||||
|
||||
`PythonRunner` 负责启动、读取和终止 Python 进程:
|
||||
|
||||
- 使用 `ProcessBuilder`,不要拼接 shell 字符串。
|
||||
- stdout/stderr 各自独立线程读取,避免进程阻塞。
|
||||
- 支持超时终止。
|
||||
- 点击停止时先发送中断标记,必要时 `destroyForcibly()`。
|
||||
- 每次运行生成 `runId`,UI 只接收当前 `runId` 的回调。
|
||||
|
||||
## 15. 异常和稳定性策略
|
||||
|
||||
| 场景 | 策略 |
|
||||
|------|------|
|
||||
| 设备未连接 | Java 层运行前阻断 |
|
||||
| 设备未授权 | 提示手机确认 USB 调试授权 |
|
||||
| APP 启动失败 | 记录 package/activity 和 ADB 输出 |
|
||||
| 元素找不到 | 自动截图,失败步骤写入 result.json |
|
||||
| 图片识别失败 | 提示阈值、模板路径、当前截图 |
|
||||
| Python 依赖缺失 | 环境检查页给出安装命令 |
|
||||
| 运行超时 | Java 层终止 Python 进程并标记 TIMEOUT |
|
||||
| 用户停止 | 标记 CANCELED,不算失败 |
|
||||
|
||||
## 16. MVP 迭代计划
|
||||
|
||||
### 16.1 第一阶段:跑通闭环
|
||||
|
||||
- 新增 `MobileUiAutomationPlugin`
|
||||
- 新增 JavaFX 嵌入式窗口
|
||||
- 实现环境检查
|
||||
- 实现设备刷新
|
||||
- 实现选择 YAML 用例并运行
|
||||
- Python Runner 支持 `tap/input/wait/assert/screenshot`
|
||||
- 生成 `result.json` 和截图
|
||||
|
||||
### 16.2 第二阶段:可用性增强
|
||||
|
||||
- 用例列表管理
|
||||
- 新建 YAML 模板
|
||||
- 图片模板目录管理
|
||||
- 失败报告 HTML
|
||||
- 支持 `.py` 和 `.air` 直接运行
|
||||
- 支持停止运行
|
||||
- 支持辅助录制回放:在插件截图面板点击、输入、滑动并生成 YAML
|
||||
|
||||
### 16.3 第三阶段:进阶能力
|
||||
|
||||
- 自动安装/管理 Python 虚拟环境
|
||||
- uiautomator2 兜底定位
|
||||
- 多设备并发
|
||||
- logcat 采集
|
||||
- APK 安装/卸载
|
||||
- 接入 CI 命令行模式
|
||||
|
||||
## 17. 命令行模式设计
|
||||
|
||||
插件实现 `getCommandName()`:
|
||||
|
||||
```text
|
||||
mobile-ui
|
||||
```
|
||||
|
||||
命令示例:
|
||||
|
||||
```text
|
||||
java -jar workutils.jar mobile-ui run --device Android:///xxx --case data/mobile-ui-auto/cases/login-flow.yaml
|
||||
java -jar workutils.jar mobile-ui devices
|
||||
java -jar workutils.jar mobile-ui check-env
|
||||
```
|
||||
|
||||
这方便后续接入 Jenkins、GitLab CI 或本地批处理。
|
||||
|
||||
## 18. 与现有项目的集成点
|
||||
|
||||
| 集成点 | 设计 |
|
||||
|--------|------|
|
||||
| 插件注册 | `@Component` + `implements Plugin` |
|
||||
| 可视化入口 | 实现 `getVisualFactory()` |
|
||||
| 配置目录 | 复用 `data/`,新增 `data/mobile-ui-auto/` |
|
||||
| 报告目录 | 复用 `export/` 或插件私有 `reports/` |
|
||||
| 日志展示 | 参考现有 GitSyncWindow 的实时日志模式 |
|
||||
| 命令执行 | 参考 GitSyncPlugin 的 `ProcessBuilder` 和输出读取模式 |
|
||||
|
||||
## 19. 风险和规避
|
||||
|
||||
| 风险 | 规避 |
|
||||
|------|------|
|
||||
| UI 测试脆弱 | 优先 Poco 元素定位,图片和坐标只兜底 |
|
||||
| 录制坐标不稳定 | 录制时同时保存 Poco 候选、图片模板和比例坐标 |
|
||||
| 手机分辨率差异 | 图片模板分设备维护,坐标按比例转换 |
|
||||
| 输入法影响文本输入 | 支持 ADB 输入、剪贴板输入、Poco set_text 多策略 |
|
||||
| 第三方依赖安装困难 | 先做环境检查和命令提示,后做虚拟环境 |
|
||||
| Python 进程卡死 | Java 层超时和强制终止 |
|
||||
| 报告不可读 | 每步结构化结果 + 失败截图 |
|
||||
|
||||
## 20. 首期验收标准
|
||||
|
||||
- 能在 Java UI 中检测 Python、Airtest、Poco、ADB 环境。
|
||||
- 能刷新并选择一台 Android 设备。
|
||||
- 能启动指定 APP。
|
||||
- 能运行一个 YAML 流程:等待页面、点击、输入、断言。
|
||||
- 能实时显示运行日志。
|
||||
- 失败时能自动截图并展示失败原因。
|
||||
- 能生成 `result.json`。
|
||||
- 支持用户手动停止运行。
|
||||
|
||||
## 21. 参考资料
|
||||
|
||||
- Airtest 官方文档:https://airtest.readthedocs.io/en/latest/
|
||||
- Airtest GitHub:https://github.com/AirtestProject/Airtest
|
||||
- Poco GitHub:https://github.com/AirtestProject/Poco
|
||||
Reference in New Issue
Block a user