首页
在线工具
搜索
1
如何将Virtualbox和VMware虚拟机相互转换
2
Markdown正确使用姿势
3
使用Metrics指标度量工具监控Java应用程序性能(Gauges, Counters, Histograms, Meters和 Timers实例)
4
Typora+Picgo图床使用
5
Kuboard与KubeSphere的区别:Kubernetes管理平台对比
杂谈与随笔
工具与效率
源码阅读
技术管理
运维
数据库
前端开发
后端开发
AI人工智能
Search
标签搜索
Angular
Docker
Phabricator
SpringBoot
Java
Chrome
SpringSecurity
Agent
SpringCloud
DDD
Git
Mac
K8S
Kubernetes
ESLint
SSH
高并发
Eclipse
Javascript
Vim
Jonathan
累计撰写
92
篇文章
累计收到
0
条评论
首页
栏目
杂谈与随笔
工具与效率
源码阅读
技术管理
运维
数据库
前端开发
后端开发
AI人工智能
页面
搜索到
1
篇与
的结果
2026-03-19
从 0 到 1 架构 Web + Mobile 端到端自动化测试平台:Playwright + Appium + TypeScript
从 0 到 1 架构 Web + Mobile 端到端自动化测试平台:Playwright + Appium + TypeScript 当一个业务同时存在 Web、Android 和 iOS 三个客户端时,自动化测试最容易走向两个极端: 一种是三套测试工程各自发展,最终基础设施重复、测试数据重复、CI 重复; 另一种是为了“统一”而设计一个万能自动化框架,试图用同一套 Driver、Element API 同时操作浏览器和移动端,最后抽象层反而比业务代码更复杂。 本文介绍一种更适合中大型项目的架构: 执行引擎分开,业务能力共享,测试基础设施统一。 一、我们到底要解决什么问题? 假设一个产品同时拥有: Web 管理后台 Web 用户端 Android App iOS App 测试团队希望自动完成下面这些业务流程: 登录 ↓ 搜索商品 ↓ 加入购物车 ↓ 创建订单 ↓ 支付 ↓ 验证订单 这时候最直接的想法通常是: Web 自动化 Android 自动化 iOS 自动化 然后分别建立三套工程。 短期没有问题。 但当测试 Case 从几十条增长到几百甚至几千条后,就会逐渐出现这些问题: 自动化测试规模扩大 │ ┌────────────┼────────────┐ │ │ │ Web Android iOS │ │ │ 一套账号管理 一套账号管理 一套账号管理 一套测试数据 一套测试数据 一套测试数据 一套 API 一套 API 一套 API 一套日志 一套日志 一套日志 一套报告 一套报告 一套报告 问题本质不是: Playwright 和 Appium 怎么一起用? 真正的问题其实是: 如何让不同终端的自动化测试共享业务能力和基础设施,但又保持各自最合适的执行模型? 二、技术选型 这套架构采用: 语言 └── TypeScript Web └── Playwright Mobile ├── WebdriverIO ├── Appium ├── Android → UiAutomator2 └── iOS → XCUITest 工程管理 └── pnpm workspace CI ├── Jenkins ├── GitLab CI └── GitHub Actions 报告 ├── Allure ├── JUnit └── 自建测试报告平台 整体关系如下: flowchart LR A[TypeScript E2E Platform] A --> B[Web Tests] A --> C[Mobile Tests] B --> D[Playwright] D --> E[Chrome] D --> F[Firefox] D --> G[WebKit] C --> H[WebdriverIO] H --> I[Appium] I --> J[UiAutomator2] I --> K[XCUITest] J --> L[Android] K --> M[iOS] 为什么统一使用 TypeScript? 并不是因为 TypeScript 一定比其他语言更适合测试。 真正的价值是: Web 和 Mobile 可以共享类型、配置、API Client、测试数据、账号池和业务模型。 三、不要构建“万能 Driver” 架构设计中最重要的一个原则是: 不要把 Playwright 和 Appium 强行抽象成同一个底层 Driver。 很多自动化框架一开始都会尝试做类似这样的东西: class Element { async click() { if (platform === 'web') { // Playwright click } if (platform === 'android') { // Appium click } if (platform === 'ios') { // XCUITest click } } } 刚开始看起来非常优雅: element.click(); element.input(); element.wait(); 似乎 Web、Android、iOS 都统一了。 但随着业务复杂度提高,很快就会变成: if Web if Android if iOS if WebView if Native if Chrome if Safari if Element Scrollable if System Permission if Keyboard Visible if Android Version if iOS Version ... 最终得到的不是一个“统一自动化框架”。 而是: 一个比 Playwright 和 Appium 本身还复杂的中间层。 四、正确的抽象边界在哪里? 真正值得共享的是: 业务场景 测试数据 账号体系 API Client 环境配置 日志 报告 测试标签 CI 调度 而不应该强行共享: Locator Element Page Driver Gesture Browser Context Mobile Session 整体架构应该是: flowchart TD A[Business Scenario] A --> B[Web Flow] A --> C[Mobile Flow] B --> D[Page Object] C --> E[Screen Object] D --> F[Playwright] E --> G[WebdriverIO / Appium] F --> H[Browser] G --> I[Android / iOS] 一句话概括: 业务层统一,执行层隔离。 五、整体工程架构 推荐将整个自动化测试平台组织成 Monorepo。 e2e-automation/ │ ├── package.json ├── pnpm-workspace.yaml ├── tsconfig.base.json │ ├── packages/ │ │ ├── core/ │ │ │ │ └── src/ │ │ ├── config/ │ │ ├── api/ │ │ ├── data/ │ │ ├── accounts/ │ │ ├── logger/ │ │ ├── retry/ │ │ ├── assertions/ │ │ └── types/ │ │ │ ├── scenarios/ │ │ │ │ └── src/ │ │ ├── login/ │ │ ├── register/ │ │ ├── search/ │ │ ├── checkout/ │ │ └── profile/ │ │ │ ├── web-tests/ │ │ │ │ ├── playwright.config.ts │ │ │ │ │ ├── src/ │ │ │ ├── pages/ │ │ │ ├── components/ │ │ │ ├── fixtures/ │ │ │ └── helpers/ │ │ │ │ │ └── tests/ │ │ │ └── mobile-tests/ │ │ │ ├── wdio.shared.conf.ts │ ├── wdio.android.conf.ts │ ├── wdio.ios.conf.ts │ │ │ ├── src/ │ │ ├── screens/ │ │ ├── components/ │ │ ├── gestures/ │ │ ├── devices/ │ │ └── helpers/ │ │ │ └── tests/ │ ├── config/ │ ├── env/ │ ├── devices/ │ └── test-suites/ │ ├── scripts/ │ ├── run-web.ts │ ├── run-mobile.ts │ ├── prepare-test-data.ts │ └── cleanup-test-data.ts │ ├── artifacts/ │ ├── screenshots/ │ ├── videos/ │ ├── traces/ │ └── logs/ │ └── reports/ 这里最关键的是: core scenarios 这两个包不会关心: Browser 是 Chrome 还是 Safari 手机是 Android 还是 iPhone 底层是 Playwright 还是 Appium 它们主要负责: 环境 账号 测试数据 API 业务模型 日志 报告 六、Web:Page Object Web 使用 Playwright。 例如登录页: import { Page } from '@playwright/test'; export class LoginPage { constructor(private readonly page: Page) {} username = this.page.getByTestId('username'); password = this.page.getByTestId('password'); submit = this.page.getByTestId('login-button'); async login(username: string, password: string) { await this.username.fill(username); await this.password.fill(password); await this.submit.click(); } } 页面对象主要负责: Locator UI Operation Page Behavior 而不要承担业务流程。 例如下面这种代码就不建议放入 LoginPage: async loginAndCreateOrder() { // ... } 因为: Page Object 表示页面能力,不应该表示完整业务流程。 七、Mobile:Screen Object Mobile 使用: WebdriverIO ↓ Appium ↓ Android / iOS 登录页面可以写成: export class LoginScreen { get username() { return $('~username'); } get password() { return $('~password'); } get submit() { return $('~login-button'); } async login(username: string, password: string) { await this.username.setValue(username); await this.password.setValue(password); await this.submit.click(); } } Android 和 iOS 如果 UI 结构高度一致,可以共享部分 Screen。 但如果两端差异比较大,建议直接拆开: screens/ ├── android/ │ ├── LoginScreen.ts │ └── CheckoutScreen.ts │ └── ios/ ├── LoginScreen.ts └── CheckoutScreen.ts 不要为了“代码复用率”牺牲可维护性。 八、真正需要共享的是 Business Flow 假设我们有一个登录流程: export interface LoginFlow { login(user: TestUser): Promise<void>; } Web 实现: export class WebLoginFlow implements LoginFlow { constructor( private readonly loginPage: LoginPage ) {} async login(user: TestUser) { await this.loginPage.login( user.username, user.password ); } } Mobile 实现: export class MobileLoginFlow implements LoginFlow { constructor( private readonly loginScreen: LoginScreen ) {} async login(user: TestUser) { await this.loginScreen.login( user.username, user.password ); } } 于是业务测试可以变成: await loginFlow.login(user); await searchFlow.search('MacBook'); await cartFlow.addProduct(); const order = await checkoutFlow.checkout(); expect(order.success).toBe(true); 测试代码关注的是: 用户做了什么 而不是: 元素怎么点 元素怎么查 页面怎么滚 九、一条测试到底是怎么运行的? 整个执行链路如下。 flowchart TD A[开始测试] A --> B[加载运行环境] B --> C[创建测试账号] C --> D[准备测试数据] D --> E{测试平台} E -->|Web| F[启动 Playwright] E -->|Android| G[启动 Appium Android Session] E -->|iOS| H[启动 Appium iOS Session] F --> I[执行业务场景] G --> I H --> I I --> J[UI Assertion] J --> K[API / DB 验证] K --> L{是否成功} L -->|成功| M[记录测试结果] L -->|失败| N[截图 / 视频 / 日志 / Trace] N --> M M --> O[清理测试数据] O --> P[生成测试报告] 这里有一个非常重要的理念: 自动化测试不是单纯的 UI 操作。 完整测试实际上是: API + Test Data + UI + Assertion + Logging + Reporting 十、测试数据必须成为一等公民 很多 E2E 自动化项目最后不稳定,并不是 Playwright 不稳定。 也不是 Appium 不稳定。 而是: 测试数据不稳定。 例如: test('用户可以购买商品', async () => { await login('test001', '123456'); }); 这种代码隐藏了大量问题: 这个账号还存在吗? 余额够吗? 是否被其他测试占用? 是否已经购买过? 有没有被封? 环境数据是否一致? 更合理的方式是: const user = await testData.createUser({ balance: 10000, status: 'active' }); 然后: await loginFlow.login(user); 测试完成以后: await testData.cleanup(user); 测试生命周期应该变成: flowchart LR A[Test Case] A --> B[API 创建用户] B --> C[API 创建商品] C --> D[API 创建测试订单数据] D --> E[UI 执行核心业务] E --> F[API / DB 验证结果] F --> G[清理测试数据] 这样 UI 自动化只验证: 必须通过 UI 才能验证的内容。 十一、账号池设计 当 CI 开始并行运行以后,会出现另一个问题: Test 01 ─┐ Test 02 ─┼── test001 Test 03 ─┘ 三个 Case 同时使用一个账号。 后果通常是: 状态污染 订单冲突 购物车冲突 登录 Session 冲突 数据互相覆盖 因此建议设计: Account Pool 运行时: flowchart LR A[Test Worker 1] --> D[Account Pool] B[Test Worker 2] --> D C[Test Worker 3] --> D D --> E[test001] D --> F[test002] D --> G[test003] 基本接口可以是: const account = await accountPool.acquire({ type: 'normal-user' }); try { await runTest(account); } finally { await accountPool.release(account); } 这样才能真正支持稳定并发。 十二、Web 浏览器矩阵 Web 可以通过 Playwright Projects 定义浏览器矩阵: export default defineConfig({ projects: [ { name: 'chrome', use: { ...devices['Desktop Chrome'] } }, { name: 'firefox', use: { ...devices['Desktop Firefox'] } }, { name: 'webkit', use: { ...devices['Desktop Safari'] } } ] }); 执行关系: flowchart LR A[Web Test] A --> B[Chrome] A --> C[Firefox] A --> D[WebKit] B --> E[Result] C --> E D --> E 但并不意味着所有 PR 都要跑所有浏览器。 测试策略应该分层。 十三、Mobile 设备矩阵 Mobile 与 Web 最大的区别是: Web 并发资源通常是: CPU Memory Browser Worker 而 Mobile 真正受限制的是: Device Simulator Emulator 因此应该构建设备池: flowchart TD A[Mobile Test Queue] A --> B[Device Scheduler] B --> C[Android 01] B --> D[Android 02] B --> E[iPhone 01] B --> F[iPhone 02] C --> G[Test Case] D --> G E --> G F --> G 设备元数据建议维护: interface Device { id: string; platform: 'android' | 'ios'; version: string; model: string; status: 'idle' | 'busy' | 'offline'; } 以后可以进一步扩展成 Device Farm。 十四、测试标签体系 当 Case 达到几百条以后,不能再依靠目录决定执行策略。 建议采用 Tag。 例如: 运行级别 @smoke @regression 优先级 @p0 @p1 @p2 业务 @login @checkout @payment @search 平台 @web @android @ios 环境 @staging @production-safe 例如: test( '@p0 @smoke @checkout 用户可以完成购买', async () => { // ... } ); 然后执行: pnpm test --tag @smoke 或者: pnpm test --tag "@p0 && @checkout" 进一步还可以实现: pnpm test:web --tag @regression 以及: pnpm test:android --tag @smoke 十五、CI 怎么设计? 不要每次 Pull Request 都执行全部 E2E。 假设: Web Case 300 Android Case 200 iOS Case 200 如果全部执行: 700 Cases × 多浏览器 × 多设备 整个 Pipeline 会非常昂贵。 建议采用三级测试策略。 flowchart TD A[Pull Request] A --> B[P0 Smoke] B -->|Pass| C[Merge] B -->|Fail| D[Block Merge] C --> E[Core Regression] E --> F[Nightly] F --> G[Full Matrix] G --> H[Chrome] G --> I[Firefox] G --> J[WebKit] G --> K[Android Devices] G --> L[iOS Devices] 推荐: Pull Request P0 Smoke 核心链路 例如: 登录 搜索 下单 支付 目标: 5 ~ 15 分钟 Merge 执行: Core Regression 例如: 核心业务 核心页面 主要设备 Nightly 每天晚上执行: Full Regression 包括: Chrome Firefox Safari Android 多版本 iOS 多版本 十六、报告体系 报告不能只有: Passed Failed 一个成熟的 E2E 平台应该记录: Test Name Platform Environment Browser Device App Version Git Commit Start Time Duration Screenshot Video Trace Console Log Appium Log Network Log Error Stack 最终报告可以呈现: Run #10231 ────────────────────────────────── Web Chrome 145 / 147 Firefox 142 / 147 WebKit 144 / 147 Android Pixel 81 / 83 Samsung 79 / 83 iOS iPhone 15 82 / 83 ────────────────────────────────── Passed 573 Failed 11 Skipped 6 整体流程: flowchart LR A[Playwright Result] --> D[Report Aggregator] B[Android Result] --> D C[iOS Result] --> D D --> E[JUnit] D --> F[Allure] D --> G[Database] G --> H[Trend Dashboard] 十七、失败的时候到底保存什么? 一个失败 Case 至少应该留下: Screenshot Video Error Stack Console Log Device Log Web 还可以保存: Playwright Trace Network Browser Console DOM Snapshot Mobile 可以保存: Appium Server Log Device Log Screen Recording Page Source 理想状态是开发者看到: Checkout failed 之后,不需要 QA 手工复现。 而是可以直接看到: 哪一步失败 失败时页面长什么样 前端报了什么错 接口有没有失败 设备有没有异常 十八、Flaky Test 怎么处理? E2E 自动化最大的敌人不是 Fail。 而是: Flaky。 也就是: 第一次失败 第二次通过 第三次又失败 如果简单粗暴地设置: retry = 3 很多问题会被掩盖。 更合理的方法: flowchart TD A[Test Failed] A --> B[Retry] B --> C{Retry Result} C -->|Fail| D[Real Failure] C -->|Pass| E[Mark Flaky] E --> F[Flaky Database] F --> G[Trend Analysis] G --> H{超过阈值?} H -->|Yes| I[创建治理任务] H -->|No| J[继续观察] 建议给每一个 Case 维护: Owner Failure Rate Flaky Rate Average Duration Last Failure 甚至可以设置: Flaky > 5% 自动通知负责人。 十九、完整自动化测试平台 随着能力逐步增加,最终整个测试平台会演进成: flowchart TB A[Developer / QA] A --> B[Test CLI] B --> C[Test Orchestrator] C --> D[Web Runner] C --> E[Mobile Scheduler] D --> F[Playwright] E --> G[Android Device Pool] E --> H[iOS Device Pool] F --> I[Business Scenario] G --> I H --> I I --> J[Test Infrastructure] J --> K[Account Pool] J --> L[Test Data] J --> M[API Client] J --> N[Config] J --> O[Logger] I --> P[Test Artifacts] P --> Q[Screenshot] P --> R[Video] P --> S[Trace] P --> T[Logs] Q --> U[Report Platform] R --> U S --> U T --> U U --> V[Trend Analysis] U --> W[Flaky Analysis] U --> X[Alert] 这时候它已经不再只是一个: 自动化脚本仓库 而是一个真正的: E2E Automation Platform 二十、推荐实施路线 如果从 0 开始,不建议一上来就做: 设备云 测试管理后台 账号中心 报表中心 调度中心 Flaky AI 分析 第一阶段最重要的是: 跑通闭环。 Phase 1:基础能力 先实现: Playwright Appium TypeScript pnpm workspace 然后选三个 Case: Login Search Checkout 要求: Web ✓ Android ✓ iOS ✓ Phase 2:基础设施 增加: Test Data Factory Account Pool API Client Environment Config Logging Artifacts Phase 3:CI 实现: PR Smoke Merge Regression Nightly Full Regression Phase 4:设备池 逐渐支持: Android Emulator Android Real Device iOS Simulator iOS Real Device Phase 5:平台化 最后才进入: Report Platform Device Farm Flaky Dashboard Trend Analysis Quality Dashboard Alert Case Owner 整个演进路线: flowchart LR A[脚本] --> B[工程化] --> C[基础设施] --> D[CI] --> E[设备池] --> F[报告平台] --> G[质量平台] 二十一、三个最值得坚持的设计原则 最后总结一下这套架构中最重要的三个原则。 1. 执行引擎分开 Web ↓ Playwright Mobile ↓ Appium 不要为了统一而统一。 2. 业务能力共享 共享: Scenario Test Data API Account Config Logger Report 而不是: Element Driver Locator 3. UI 只负责验证 UI 能够通过 API 完成的: 创建用户 创建订单 创建商品 初始化数据 清理数据 尽量不要通过 UI 做。 UI 自动化应该把时间投入到: 用户真正看到和操作的关键路径 二十二、最终架构 最终推荐的结构可以总结为: E2E Automation Platform │ ┌───────────┴───────────┐ │ │ Playwright Appium │ │ Web WebdriverIO │ ┌──────┴──────┐ │ │ Android iOS UiAutomator2 XCUITest ────────────────────────────────────────────── Shared Infrastructure ────────────────────────────────────────────── TypeScript Business Scenario Test Data Factory Account Pool API Client Environment Config Logging Reporting Tagging Artifacts CI Orchestrator Device Scheduler 架构原则可以浓缩成一句话: 执行引擎分开,业务能力共享,基础设施统一。 当项目只有十几条自动化 Case 时,架构可能并不重要。 但当 Case 达到: 100 500 1000+ 真正决定这个项目能不能继续维护的,已经不是: 某一个 Locator 怎么写 而是: 测试数据如何管理? 设备如何调度? 测试如何并发? 失败如何定位? Flaky 如何治理? 多平台如何共享业务能力? CI 如何控制执行成本? 这也是自动化测试从: “写脚本” 走向: “工程化” 再走向: “平台化” 最关键的一步。
2026年03月19日