从婚礼请柬App说起:我的移动测试自动化实战之路
上个月,我一边在公司赶着上线新功能,一边还在淘宝上疯狂对比喜糖盒的材质——没错,作为一个刚入职新公司两个月、正处在备婚冲刺阶段的程序媛,我的日常就是代码和婚纱照修图软件无缝切换。MacBook Pro 上 Xcode 和 Photoshop 轮流开,Windows 虚拟机只在测兼容性时才勉强露个脸。
事情的转折点发生在我妈第 N 次问我:“你那个电子请柬小程序到底能不能用啊?隔壁老王家闺女都发完了!”——我做的婚礼请柬 H5 小程序(顺手用 React Native 写了个壳),因为没做充分测试,上线后安卓低端机白屏、iOS 14 点击无响应,差点让我在亲戚面前社死。那一刻我下定决心:再也不能靠手动点点点来保证质量了。
于是,在新公司的第一个大版本迭代前夕,我主动跟技术负责人提了搞移动测试自动化的想法。毕竟,谁不想把周末还给试婚纱呢?
为什么手动测试救不了我们?
我们团队目前维护一个跨平台电商 App,日活不算高,但促销节点密集(比如刚过去的 618)。每次上线前,QA 同学都要在十几台真机上反复点购物流程、支付、优惠券核销……上周五晚上十点,我还在办公室帮测试小哥复现一个“仅在华为 P30 + Android 10 + 横屏模式下才会触发的地址选择器弹窗错位”问题。他眼睛都快贴到屏幕上了,我看着都心疼。
更离谱的是,有一次产品经理临时改了需求,说“加个分享按钮就行”,结果前端改完没测 iOS 分享回调,上线后用户分享成功却无法跳回原页面——直接导致当天分享转化率暴跌 40%。运维老哥在群里哀嚎:“你们再这样,我就要写脚本自动回滚了!”
这些血泪教训让我意识到:在快节奏迭代中,靠人肉覆盖所有路径几乎不可能。尤其当你的用户分布在从 iPhone SE 到 Redmi Note 的广阔机型光谱上时。
选型:别被“全栈”忽悠了
一开始我也被各种工具迷了眼:Appium、Detox、Espresso、XCUITest、Playwright Mobile……官方文档写得天花乱坠,仿佛用了就能一键解放生产力。但现实是,每种方案都有它的“脾气”。
我在 Mac 上搭环境时就踩了坑。Appium 虽然跨平台,但对 iOS 真机调试依赖 WebDriverAgent,而 Apple 每次 Xcode 升级都可能把它搞崩。有次我折腾了整整一个下午,最后发现是因为 Xcode 14.3 默认禁用了某些权限——当时真的想砸键盘。
经过一番调研和内部 PoC(Proof of Concept),我们最终选择了 分层策略:
- UI 层:iOS 用 XCUITest(Swift 编写),Android 用 Espresso(Kotlin),原生支持好、运行快
- 业务逻辑层:用 Jest + React Native Testing Library 覆盖核心流程
- E2E 跨平台验证:保留少量关键路径用 Appium 做兜底(比如登录 → 加购 → 支付)
💡 开发心得:不要迷信“一套脚本跑全平台”。原生测试框架虽然要写两套,但稳定性高、调试友好,长期看反而省时间。毕竟,我们的目标是可维护性,不是炫技。
下面是我们最终采用的技术栈对比:
| 维度 | XCUITest / Espresso | Appium | Detox |
|---|---|---|---|
| 语言 | Swift / Kotlin | 多语言(JS/Python等) | JS(Jest) |
| 执行速度 | ⚡️ 极快(ms级) | 🐢 较慢(s级) | 🏃♀️ 快 |
| 真机支持 | ✅ 官方原生 | ✅ 依赖驱动 | ❌ 仅模拟器 |
| 调试体验 | 🔍 Xcode/Android Studio 直接断点 | 🕵️♂️ 日志复杂 | 🧪 Jest 集成好 |
| 学习成本 | 中(需了解平台) | 低(Web 自动化经验可迁移) | 中 |
实战:让自动化真正跑起来
第一步:从最痛的场景切入
我没有一上来就追求 100% 覆盖率,而是先解决高频出问题的路径。比如我们的“新人专享礼包领取”流程,过去一个月出了三次 Bug:一次是倒计时未重置,一次是礼包未到账,还有一次是分享后状态不同步。
我用 XCUITest 写了这样一个测试用例(简化版):
func testNewUserCouponFlow() throws {
let app = XCUIApplication()
app.launchArguments = ["UITest_NewUserMode"]
app.launch()
// 模拟新用户首次打开
XCTAssertTrue(app.buttons["立即领取"].exists)
app.buttons["立即领取"].tap()
// 验证弹窗出现
let shareSheet = app.sheets["分享邀请"]
XCTAssertTrue(shareSheet.exists)
// 模拟点击微信分享(不真调起)
shareSheet.buttons["微信"].tap()
// 验证返回后礼包已激活
XCTAssertTrue(app.staticTexts["礼包已生效"].exists)
}
关键技巧是通过 launchArguments 注入测试模式,绕过真实登录和网络请求,直接进入目标状态。这大大提升了执行速度和稳定性。
第二步:搞定 Android 的碎片化地狱
如果说 iOS 是整齐划一的花园,那 Android 就是野生丛林。同一个 UI 元素,在三星、小米、OPPO 上的 accessibility ID 可能完全不同。
我们的解法是:强制规范开发侧的测试标识。
在组件库中,我们约定所有可交互元素必须带 testID(React Native)或 android:testId(原生),并在 CI 流程中加入检查:
# 在 PR 检查中跑这个脚本
grep -r "onPress=" src/ | grep -v "testID=" && echo "⚠️ 发现未标注 testID 的可点击元素!" && exit 1
虽然一开始前端同事有点抵触(“多写一行代码好麻烦”),但自从 QA 不再半夜 @ 他们问“这个按钮叫啥名字”之后,大家就真香了。
第三步:集成到 CI,让测试说话
光本地跑没用,必须融入发布流程。我们在 GitLab CI 里配置了这样的流水线:
e2e_ios:
stage: test
script:
- xcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 14'
only:
- merge_requests
e2e_android:
stage: test
script:
- ./gradlew connectedDebugAndroidTest
only:
- merge_requests
现在,任何 MR(Merge Request)如果导致 E2E 测试失败,GitLab 会直接 block 合并——连产品经理都不能 override(这点我特意跟 PM 谈好了,他说“只要不影响上线时间,随你搞”)。
成果与反思:省下的不只是时间
上线这套自动化体系三个月后,我们看到了实实在在的变化:
- 回归测试时间从平均 4 小时缩短到 15 分钟
- 线上 P0/P1 级 Bug 下降 60%
- 我终于能在周五晚上准时下班去试婚纱了(感动哭)
但也有教训。比如一开始我们试图用图像识别来做“视觉回归测试”,结果发现不同机型渲染字体微差就导致大量误报;又比如 Appium 在 CI 环境偶尔卡死,排查半天发现是模拟器内存不足……
综合来看,自动化测试不是银弹,但它能把你从重复劳动中解放出来,去做更有价值的事——比如优化用户体验,或者……专心筹备婚礼?
给同行的几句真心话
- 从小处着手:别一上来就想覆盖所有页面。先抓核心路径,跑通再扩展。
- 可读性 > 技巧性:测试代码也是代码!命名清晰、结构合理,半年后你自己还能看懂。
- 和 QA 深度合作:他们最清楚哪些地方容易出问题。我们的测试用例很多来自 QA 提供的“Bug 场景清单”。
- 接受不完美:有些动态内容(比如推荐流)很难自动化验证,那就留给人眼。自动化的目标是减少重复错误,不是消灭所有人工。
写这篇文章的时候,我刚收到婚庆公司发来的最终版请柬预览——这次,我在测试环境跑了完整的分享+打开流程,确认在 iPhone 12 和 Redmi K50 上都能正常显示。终于,可以安心地把精力放回代码和婚礼细节上了。
毕竟,作为一个程序媛,我既要写出健壮的系统,也要拥有一场完美的婚礼。而自动化测试,就是帮我实现这两者的秘密武器。
(完)
后记:如果你也在备婚+coding 双线作战,欢迎留言交流!说不定我们还能拼个喜糖团购 😄

评论 0