
大家好,我是洋子
自从去年底转AI测试工程师后,到现在我的工作模式已经有了非常大的转变
除了与RD、PM等协作角色沟通,E2E测试方面还有一部分在人工执行,其他的日常工作已全部交给AI去完成,包括生成功能/接口自动化/UI自动化测试用例,设计技术方案,开发测试工具,理解业务架构等等
这种工作模式已经持续了大半年了,可以说,再让我脱离AI,我都不会干活了
AI目前在团队内部已经完成了单点提效,比如开发用AI 写代码,测试用AI生成测试用例等,但这个过程还是需要人通过Prompt与AI对话来驱动
能不能改造成AI 自动完成上面的工作,人只来做决策,工作流全自动化流转起来
那么,要实现全自动化的前提,就是需要把CI/CD 基建改造成Agent 或者能让Agent 能更方便的调用流水线上的Job,另外就是尽量把工作SOP化成Skill
本文主要跟大家分享两方面
在开始正题之前,先来回顾一个项目需求交付模式,在2025年以前,每当来了一个新需求,基本上都是人在执行工作
产品经理先写PRD需求文档,UI设计画视觉稿,前后端开发设计技术方案+人工写代码,QA设计测试用例并执行,测试完成后上线
各角色的工作流程都是人来决策+执行

从2025年到现在,越来越多团队的工作模式变成了先由AI(如Claude Code、Codex等)来设计方案,人再通过对话的方式来调整方案,方案确定后再由AI来完成执行工作
AI负责决策+执行,部分互联网大厂已经自研出开发Agent,测试Agent等来代替人工执行开发和测试执行工作,但Agent执行过程中还是需要人与其对话进行驱动

而未来的工作模式,会以Agent为核心,自动去完成上述工作,彻底脱离人,人在整个过程的角色则变成了维护知识库供AI参考

有小伙伴会好奇,那Agent底层技术都是用的什么?基座大模型一般选择最新的模型,如GLM-5.2 ,DeepSeek-V4,Claude Opus 4.8,GPT-5.6等
上层的Agent根据业务场景来选择,如果是一些中小创业公司,AI赋能内部的工作流直接用Codex、Claude Code即可,也有选择OpenClaw来封装的
对于定制化的Agent,可以用LangGraph、LangChain、Dify等框架来实现
Claude Code与Codex,本质就是通用Agent,结合自己开发的各种Skill能快速搭建出适配团队内部的Agent
下面将重点介绍Codex+Skill做的测试Agent,用来全自动化执行APP 自动化测试
对于执行APP 自动化测试,真正的难点往往不是写出第一条自动化测试用例,而是下面这些问题
这些步骤只要还有一半依赖人工,APP自动化测试就很容易变成半自动化。
我现在做的这套方案,核心思路是把 GitLab CI、任务队列、真机节点和 Codex Skills 串成一个完整闭环
测试人员只需要先在 GitLab CI流水线场景测试任务,填写代码模块、代码分支、回归模式和 PRD文档路径等必要参数
Mac Mini本地自动监测到新的测试任务后,自动领取测试任务,开始生成Plan测试计划,后续进行代码分析、APK 构建安装、自动化用例生成、真机执行和结果通知
执行过程中不需要人工干预,任务执行失败/成功后,钉钉机器人会自动通知任务执行结果

要搭建起整套核心工作流,有两个核心服务
ui-test-task-server:管理(查询/新增/更新)测试任务,管理任务队列ui-test-task-client:执行端,调用ui-test-task-server的接口,领取测试任务,之后调用Codex CLI和一系列Skill完成测试任务部署环境注意事项:
ui-test-task-server部署在任意一台Linux服务器,ui-test-task-client运行在本地PC或Mac Mini(执行APP 自动化需要使用ADB命令操作真机,SDK依赖等限制)
涉及的Skill汇总如下:

这套方案解决的不是单纯的用例生成问题,而是把任务创建、代码分析、APK 构建安装、用例生成、真机执行和结果通知串成一条可持续运行的流水线。
测试人员只需要在 GitLab 创建任务。部署在 Mac Mini 上的 ui-test-task-client 会自动领取任务,并根据任务参数调用对应的 Skill 完成后续工作。
Linux服务器上需要提前准备部署 ui-test-task-server 服务并能正常访问

Mac Mini 需要提前准备以下环境。
android-ui-tests 仓库(含APP自动化测试工程文件)
~/.codex/skillsGITLAB_TOKEN 已配置先检查基础环境。
adb devices -l
codex --version
python3 --version
ADB 列表中的设备状态必须是 device。如果显示 unauthorized 或 offline,Client 不会领取任务。
先将ui-test-task-client工程文件拉取在Mac Mini上

在仓库根目录创建本地环境文件。
cp ui-test-task-client/client.env.example \
ui-test-task-client/client.env
chmod 600 ui-test-task-client/client.env
填写必要配置。
export OPENAI_API_KEY="your-openai-api-key"
export UI_TASK_SERVER_URL="http://your-ui-task-server:8091"
export ANDROID_HOME="$HOME/Library/Android/sdk"
export ANDROID_SDK_ROOT="$HOME/Library/Android/sdk"
export GITLAB_TOKEN="your-gitlab-token"
client.env 已被 Git 忽略,不要把 API Key、GitLab Token 或设备认证密码提交到代码仓库。
启动前可以打印一次配置。
python3 ui-test-task-client/ui_test_task_client.py --print-config
只消费一轮任务用于调试。
python3 ui-test-task-client/ui_test_task_client.py --once
正常启动常驻轮询。
python3 ui-test-task-client/ui_test_task_client.py
生产使用建议配置成 macOS LaunchAgent。进程退出后由 launchd 自动拉起,Mac Mini 重启后也能自动恢复。
进入 GitLab 项目的 Build → Pipelines → New pipeline,填写 UI 自动化任务参数,然后手动执行 create_ui_test_task Job。


常用参数如下。
参数 | 示例 | 用途 |
|---|---|---|
code_refs | {"uniclaw-server":"feature/a","uniclaw-android":"dev"} | 指定代码模块与分支 |
module_kind | auto | 自动识别 server、client 或 mixed |
regression_strategy | changed | 选择 auto、smoke、full 或 changed |
prd_path | android-ui-tests/docs/pm/im.md | 可选 PRD 文件 |
require_explicit_prd | false | 是否禁止自动查找历史 PRD |
auto_test_args | --skip-sync --skip-dump | 控制 PRD 全流程中的跳过项 |
GitLab Job 会调用 create_ui_test_task.sh,把任务写入 ui-test-task-server。任务初始状态为 pending,数据保存在 MySQL 的 ui_test_tasks 表中。
ui-test-task-client 默认每 10 秒轮询一次。
每轮处理顺序如下。
adb devices 检查真机状态pending 任务/v1/ui-test-tasks/claim 原子领取最早任务runningclaimed_by 和 claimed_atUI 自动化任务开始执行
• client: lucasdeMacBook-Air.local
• host: lucasdeMacBook-Air.local
• workspace: /Users/lucas/xxx-test
任务信息
• id=19
• project=xxx-test
• branch=ui_test
• commit=af17f7
• created_at=2026-07-14T15:45:09.233
• module_kind=auto
代码模块/分支
• xxx-server: release-20260701
• xxx-android: dev
原子领取可以防止多台 Mac Mini 同时执行同一个任务。
如果存在待执行任务但没有可用真机,Client 不会领取任务,而是发送钉钉报警。超过 24 小时仍未执行的积压任务,每天还会额外提醒一次。
领取任务后,Client 会先调用 android-ui-task-orchestrator 中的确定性规划脚本。
python3 skills/android-ui-task-orchestrator/scripts/plan_task.py \
android-ui-tests/data/task-inputs/task-{task_id}.json
规划器会完成以下工作。
code_refsfull、smoke 或 changed完整计划写入以下目录。
android-ui-tests/data/task-plans/task-{task_id}-plan.json
如果计划要求使用新包,Client 会在启动 Codex 前执行 APK 前置流程。
先调用 android-apk-build。
android-apk-build
→ 触发 Android GitLab Pipeline (根据自身业务流程调整Android端打包)
→ 触发钉钉通知
→ 等待 APK 构建 Job
→ 获取 APK 下载地址和版本信息
构建开始的钉钉通知内容
UI 自动化前置 APK 构建开始
• id=19
• project=xxx-test
• branch=ui_test
• commit=af17f722
• created_at=2026-07-14T15:45:09.233
• module_kind=auto
• branch: dev
• environment: prod
构建成功的钉钉通知内容
UI 自动化前置 APK 构建完成,开始安装
• task: 19
• branch: dev
• version: 1.0.5 (105)
• download_url: https://xxxx/app/download/fe128b2a
• job_url: http://xxx/chagent/xxx-android/-/jobs/1840732
安装APK完成的钉钉通知内容
UI 自动化前置 APK 安装完成
• task: 19
• device: LKN5T18A28017235
• package: com.xxx.xxx
• installed_version: 1.0.5 (105)
• next: start Android UI automation
构建环境根据模块组合决定。
代码模块 | APK 策略 |
|---|---|
包含 server 或 gateway | 构建 test 预发包 |
只有 uniclaw-android | 构建 prod 正式包 |
server + Android | 使用传入 Android 分支构建 test 包 |
server-only | 默认使用 Android dev 分支构建 test 包 |
构建完成后调用 android-ui-install-apk。
android-ui-install-apk
→ 解析真实下载地址
→ 下载并校验 APK
→ adb install
→ 处理 Android 或华为安装提示
→ 校验 versionName 和 versionCode
→ 启动 App
只有真机版本与本次构建结果一致,任务才会继续。同一 Task 重试时,如果分支、环境和 Pipeline SHA 都没有变化,可以复用已有构建结果。
APK 准备完成后,Client 启动 Codex CLI。
codex exec \
-C "$UI_TASK_WORKSPACE" \
--dangerously-bypass-approvals-and-sandbox \
--json \
"<task prompt>"
Codex 首先调用 android-ui-task-orchestrator。它根据测试计划选择后续 Skill,不会无条件读取所有生成规范。
Smoke 只执行带有 P0 和 smoke 标记的存量用例。
android-ui-task-orchestrator
→ android-ui-run-and-fix
→ Android 真机
这条路径不会调用 PRD 解析和用例生成。
Full 无 PRD 时,直接执行全部存量测试。
Full 带 PRD 时,调用 android-ui-auto-test,执行完整 PRD 流程。
android-ui-sync-bug-history
→ android-ui-dump-ui
→ android-ui-prd-parse
→ android-ui-gen-tests
→ android-ui-run-and-fix
→ android-ui-analyze-results
可以通过 --skip-sync 或 --skip-dump 复用已有数据,减少执行时间。
Changed 模式先调用 android-ui-changed-regression。
它会拉取指定代码仓库,对比基准分支和任务分支,并把变化文件映射到消息、群聊、联系人、个人资料等测试模块。
android-ui-task-orchestrator
→ android-ui-changed-regression
→ git clone / fetch / diff
→ 计算影响范围
如果存量用例已经覆盖变化范围,直接执行对应 Marker。
如果需要生成新用例并且找到了可用 PRD,则进入生成链路。
android-ui-dump-ui
→ android-ui-prd-parse
→ android-ui-gen-tests
→ android-ui-run-and-fix
如果没有上传新 PRD,系统可以从 android-ui-tests/docs/pm 自动匹配历史 PRD。找不到可信 PRD时,不会生成占位用例,而是执行已有的受影响用例,并记录未生成原因。
android-ui-gen-tests 会综合以下信息生成测试代码。
生成目录如下。
android-ui-tests/tests/generated/task_{task_id}/
生成后会先执行静态验证。
python py_compile
pytest --collect-only
只有能够正常编译和收集的测试,才允许进入真机执行阶段。
android-ui-run-and-fix 使用 Pytest、Appium 和 UiAutomator2 驱动真机。
它会收集日志、截图和 Allure Results,并区分以下问题。
Changed 和 Smoke 默认只进行一轮聚类修复,并精确复测当前失败 Node ID,不会反复执行整套 Case。
执行结束后,android-ui-analyze-results 解析 Allure 数据,生成失败分类和 Bug 信息。确认属于产品缺陷时,可以继续调用 yunxiao-bug 提交云效工作项。
无论 Codex 是否正常退出,Client 最后都会调用 finalize_task.py。
它会生成两份固定产物。
android-ui-tests/docs/manual-cases/task-{id}-manual.md
android-ui-tests/docs/task-reports/task-{id}-report.md
人工回归清单记录自动化未覆盖、跨设备、环境受限或缺少可靠 Dump 的场景。
任务报告记录输入参数、代码影响范围、执行结果、失败原因、踩坑经验、产物位置和后续优化建议。
最后,Client 根据 Codex 退出码、测试汇总和计划错误综合判断任务状态,将结果更新为 success 或 failed,并发送钉钉通知。失败通知会附带最多三条失败 Case 的前置条件、执行步骤、预期结果和失败原因。
任务完成后的钉钉通知内容
### UI 自动化任务执行失败
**执行节点**
- client: lucasdeMac.local
- host: lucasdeMac.local
- status: failed
- message: Codex CLI 以 exit code 1 异常退出,Pytest 在约 35% 时中断,未完成全部测试和最终结果汇总
**任务信息**
- id: 19
- project: xxx/xxx/xxx-test
- branch: ui_test
- commit: af17f722
- created_at: 2026-07-14T15:45:09.233
- module_kind: auto
- regression_strategy: changed
- Android 设备: EVR_AL00
- APK 版本: 1.0.5(105)
**测试汇总**
- 收集用例: 515
- 执行进度: 约 35%
- 已记录失败: 3
- 最终汇总: 未生成
- 中断原因: Codex CLI 在 Pytest 完成前异常退出
- Allure Results: `data/allure-results-task19_changed-20260714_163439`
- 运行日志: `data/test_results/run_20260714_163439.log`
**失败 Case**
1. `L1-001 添加好友页元素组存在`
- 失败原因: 添加好友页标题不存在
- 失败断言: `AddFriendPage.TITLE` 定位结果为空
2. `L1-002 通讯录页面元素组存在`
- 失败原因: 通讯录标题不存在
- 失败断言: `ContactsPage.TITLE` 定位结果为空
3. `L1-003 扫一扫页面元素组存在`
- 失败原因: 扫一扫页面标题不存在
- 失败断言: `ScanQRPage.TITLE` 定位结果为空
**失败分析**
- 3 条失败集中在添加好友模块的页面标题和前置导航
- 同模块后续搜索、扫一扫入口、好友详情等多数用例执行成功
- ADB、Appium 和 UiAutomator2 运行正常
- 初步归类为页面导航或 Locator 契约失效
- 暂未确认属于产品功能缺陷
- 需要刷新通讯录、添加好友和扫一扫页面的 UI Dump 后重新核对 Locator
**人工复现步骤**
前置条件:
- 使用已登录测试账号
- 安装 APK 1.0.5(105)
- App 位于主界面
操作步骤:
1. 启动 xxx App
2. 点击底部「通讯录」Tab
3. 检查页面顶部是否展示「通讯录」标题
4. 点击通讯录页面右上角添加好友按钮
5. 检查页面是否进入「添加好友」页面
6. 检查标题、搜索框和「扫一扫」入口是否展示
7. 点击「扫一扫」
8. 如出现相机权限弹窗,允许权限或关闭弹窗
9. 检查扫一扫页面标题和扫描提示是否展示
预期结果:
- 通讯录、添加好友和扫一扫页面均能正常进入
- 页面标题和关键操作入口正确展示
实际结果:
- 自动化未找到「通讯录」「添加好友」「扫一扫」页面标题
- 需要结合失败截图和最新 UI XML 判断是页面未跳转,还是 Locator 已失效
**建议处理**
1. 执行 `android-ui-dump-ui` 更新三个页面的 UI XML
2. 检查 `ContactsPage.TITLE`、`AddFriendPage.TITLE` 和 `ScanQRPage.TITLE`
3. 修复前置导航或 Locator
4. 仅复测以上 3 条失败 Node ID
5. 排查 Codex CLI 提前退出原因,避免测试未完成却缺少最终 summary
到这里,一次完整的 UI 自动化任务才算真正结束。
整条主流程可以概括为。
GitLab 创建任务
→ ui-test-task-server 写入 pending 任务
→ Mac Mini Client 领取任务
→ 生成测试计划
→ 构建并安装 APK
→ Codex 调用 Orchestrator
→ 选择 full / smoke / changed
→ 必要时生成新用例
→ Appium 真机执行
→ Allure 结果分析
→ 生成任务报告
→ 更新任务状态
→ 钉钉通知