移动应用测试自动化,35岁老码农的翻车与重生
上周五晚上十点半,我戴着耳机,单曲循环着 Radiohead 的《Karma Police》,手指在 MacBook 上敲出最后一行等待代码。窗外是上海徐汇区写字楼里永不熄灭的灯光,隔壁工位的实习生刚走,留下一罐没喝完的元气森林。而我,一个35岁还在写代码的老程序员,正被一个 UI 自动化测试脚本折磨得想把键盘扔进黄浦江。
事情是这样的:我们团队最近在做一款主打海外市场的电商 App,双11前要上线新功能,但 QA 同学天天在群里哀嚎:“手动测一轮要三天,版本又提测了,求求你们给点时间!”产品经理更是神补刀:“这个按钮点击后跳转不对,昨天不是修好了吗?怎么又坏了?”
那一刻,我突然意识到——再不搞自动化测试,我们这帮人怕是要在 2024 年底前集体猝死在工位上。
于是,我决定亲自下场,把移动应用测试自动化搞起来。毕竟,作为一个租房住公司附近、每天靠咖啡和代码续命的中年开发者,我早就受够了“线上回滚”、“紧急 hotfix”这些词儿。
为什么手动测试越来越扛不住?
先说说我们的现状。App 是 React Native 写的,iOS 和 Android 双端都有,每周至少两个版本提测。以前靠 QA 手动点点点,勉强能撑住。但随着业务复杂度飙升,页面跳转链路动辄七八层,加上各种网络状态、权限弹窗、设备适配……光是“登录-加购-结算”这一条路径,就得在十几种机型上跑一遍。
更惨的是,有一次因为某个组件在 Android 13 上渲染错位,导致用户无法点击“确认支付”,结果上线后半小时内退款率飙升。老板在周会上黑着脸问:“这种低级问题为什么没测出来?”QA 小哥委屈得快哭了:“我们只有一台 Pixel 7,其他都是模拟器啊……”
是的,现实就是这么骨感。手动测试不仅慢,还容易漏。尤其是在我们这种“敏捷开发、快速迭代”的节奏下,测试成了瓶颈中的瓶颈。
初探自动化:从 Appium 开始,但差点翻车
我一开始信心满满,觉得不就是写个脚本自动点按钮嘛,能有多难?于是翻出尘封已久的 Appium 教程,搭环境、写 capability、启动 driver,一顿操作猛如虎。
结果第一天就卡在“元素定位”上。React Native 的 View 在 iOS 上叫 XCUIElementTypeOther,在 Android 上又是 android.widget.FrameLayout,而且层级嵌套深得像俄罗斯套娃。更坑的是,有些按钮根本没有 accessibility ID,只能靠 XPath 硬怼,可一旦 UI 微调,XPath 就全崩。
# 这段 XPath 我写了整整两小时,第二天 UI 改了个 padding,直接失效
driver.find_element(By.XPATH, "//android.widget.FrameLayout[1]/android.widget.LinearLayout[2]/android.widget.Button[1]")
那几天我做梦都在背控件树。同事看我眼神都变了:“老张,你是不是压力太大了?要不要去 HR 那边聊聊?”
更要命的是,Appium 跑真机时经常卡死,日志里全是 Could not proxy command to remote server。我查了 Stack Overflow,有人说是因为 USB 调试不稳定,有人说要升级 WebDriverAgent,还有人建议直接换 Mac mini 搭 Jenkins —— 听起来都像在劝我转行。
转机:Claude Code v0 带来的“偷懒”新思路
就在我不知所措时,偶然在 GitHub Trending 上看到一个叫 Claude Code v0 的项目(注意:这不是 Anthropic 的 Claude,而是社区开源的一个轻量级测试辅助工具,名字蹭了热度,但确实好用)。
它干了件特别聪明的事:不直接操作 UI,而是通过埋点 + 逻辑校验来间接验证行为。
比如,我们原本要测试“点击购物车图标后跳转到购物车页”,传统做法是找图标元素、点击、等新页面加载、再找“购物车”标题。但 Claude Code v0 的思路是:
- 在点击事件触发时,前端主动上报一个
event: cart_clicked - 测试脚本监听这个事件
- 同时检查当前路由是否变为
/cart - 如果两者匹配,就认为测试通过
这样就绕开了脆弱的 UI 定位问题,稳定性直线上升。
我试着在项目里加了几行埋点:
// React Native 组件中
const handleCartPress = () => {
analytics.logEvent('cart_clicked'); // 上报事件
navigation.navigate('Cart');
};
然后在测试脚本里:
from claude_code_v0 import TestClient
client = TestClient(app_id="com.example.shop")
client.start_monitoring()
cart_button.click()
assert client.wait_for_event("cart_clicked", timeout=5)
assert current_route() == "/cart"
跑起来居然一次成功!那天晚上我激动得喝了三杯美式,差点心悸。
真实落地:从“玩具”到“生产线”的跨越
当然,理想很丰满,现实还得搬砖。要把这套东西用在生产环境,得解决几个硬骨头:
1. 多平台适配怎么办?
Claude Code v0 本身是跨平台的,但它依赖的埋点方案得统一。我们最后决定:
- iOS 用 Firebase Analytics
- Android 用自研的轻量 SDK(为了合规)
- Web 用 GA4
但事件命名必须一致,比如都叫 cart_clicked。为此我们建了个内部 Wiki,强制所有 FE/移动端同学按规范打点。
2. 性能会不会拖慢 App?
这是老板最关心的问题。我们做了 A/B 测试:
| 方案 | 冷启动耗时 (ms) | 内存占用增量 | 包体积增加 |
|---|---|---|---|
| 无埋点 | 1200 | 0 MB | 0 KB |
| 全量埋点 | 1280 | +3 MB | +180 KB |
| Claude Code v0(仅测试环境) | 1210 | +0.5 MB | +40 KB |
结论:只要在 release 构建时关掉测试埋点,几乎无影响。我们用 __DEV__ 和构建 flag 控制:
if (__DEV__ && process.env.TEST_MODE) {
analytics.logEvent(...);
}
3. 如何集成到 CI/CD?
我们用的是 GitLab CI,每提交 PR 就自动跑核心路径的自动化测试。配置如下:
test_mobile_e2e:
stage: test
script:
- npm run build:android:test
- python scripts/run_claude_tests.py --platform android
- python scripts/run_claude_tests.py --platform ios
artifacts:
paths:
- test-reports/
only:
- merge_requests
现在,只要 PR 里改了购物流程,CI 就会自动跑 10 条核心路径。如果失败,直接 block 合并。产品经理再也不敢说“这个小改动应该没问题”了。
成果:从“救火”到“预防”
上线三个月后,数据说话:
- 核心路径回归测试时间从 3 天 → 45 分钟
- 线上 P0 级 Bug 下降 68%
- QA 同学终于有时间做探索性测试,而不是重复点点点
- 我自己……嗯,至少不用再半夜被 PagerDuty 叫醒
最让我感动的是,上周 QA 主管请我喝奶茶,说:“老张,你这玩意儿救了我们全家。”(夸张了,但意思到了)
给新手的几点真心话
如果你也正被移动测试折磨,别慌,分享几个血泪经验:
- 别一上来就追求 100% 覆盖。先搞定最核心的 3-5 条路径(比如登录、下单、支付),这些路径出问题代价最高。
- UI 自动化是最后手段。能用接口测就用接口,能用逻辑校验就别碰 UI。Claude Code v0 的思路值得借鉴。
- 真机 > 模拟器。尤其涉及权限、通知、后台唤醒等场景,模拟器根本测不准。我们后来买了个二手 iPad 和 Pixel 6 当“测试专用机”。
- 让开发参与埋点。测试不是 QA 一个人的事。我们团队现在要求 PR 必须包含对应测试用例,否则不 merge。
- 别信“一键生成脚本”。市面上有些工具号称能录制操作自动生成代码,但维护成本极高,UI 一变全废。
最后:35岁还在写代码,图个啥?
有人问我,都这年纪了,干嘛还折腾新技术?不如混混日子等退休。
但我觉得,写代码最爽的不是炫技,而是解决问题。当看到自己写的测试脚本在凌晨三点自动跑完,发现了一个潜在的支付漏洞,那种“我保护了用户的钱包”的感觉,比涨工资还爽。
而且,学 Claude Code v0 的过程,也让我重新找回了刚入行时的热情。原来,35岁不是终点,而是换个姿势继续跑。
对了,最近我在用业余时间学 LLM + 自动化测试结合,比如用 AI 生成测试用例。虽然还不成熟,但想想就兴奋——或许下次写文章,就是《用大模型给 App 做“体检”》了。
耳机里,Radiohead 唱到:“This is what you get when you mess with us…”
我笑了笑,保存代码,按下运行键。
夜还长,bug 还多,但至少,我们不再裸奔。

评论 0