Mobile App Testing 入門 — Appium vs Detox 怎麼選、跨平台策略、device farm
「網站測試我會、mobile 也差不多吧」是新人最大的誤解。Mobile testing 是另一個物種 — 不同 OS、不同裝置尺寸、不同網路、推播、相機、權限、deep link… 痛點是 web 的 3-5 倍。這篇講 QA 從 web 跨到 mobile 該注意什麼。
三種 App 你要先分清楚
flowchart TD
App[Mobile App] --> Native[原生 Native]
App --> Cross[跨平台 Cross-platform]
App --> Hybrid[Hybrid / WebView]
Native --> N1[iOS: Swift<br>Android: Kotlin/Java]
Cross --> C1[React Native<br>Flutter<br>Xamarin]
Hybrid --> H1[Cordova / Ionic<br>Capacitor<br>WebView 包裝]
style Native fill:#06b6d4,color:#fff
style Cross fill:#10b981,color:#fff
style Hybrid fill:#a855f7,color:#fff
| 類型 | 例子 | 測試難度 | 自動化選 |
|---|---|---|---|
| Native | Twitter (early)、Uber、銀行 App | 高(要兩套) | XCUITest + Espresso 或 Appium |
| Cross-platform | Discord、Bloomberg、Meta | 中 | Detox(RN)/ Patrol(Flutter)/ Appium |
| Hybrid | 多數中小 App | 低 | Appium / 通用 web 工具 |
測試前先問:「我們是哪種 App?」 — 工具選錯後面會吐血。
Mobile vs Web 的 5 大差異
mindmap
root((Mobile<br>Testing<br>痛點))
OS 碎片化
iOS 多版本
Android 廠商客製
OEM 修改 UI
硬體變數
螢幕尺寸
解析度
網路 4G/5G/WiFi
電量
系統互動
推播
權限
Deep link
Universal link
輸入方式
Touch gesture
鍵盤切換
手寫
Voice
平台特性
iOS Face ID / Touch ID
Android 多視窗
Background restriction
App lifecycle
1. OS / 裝置碎片化
- iOS:相對單純,但 iOS 16/17/18 + iPad / iPhone SE/15 Pro Max 都要測
- Android:地獄。Samsung S24、Pixel 8、小米 14 都有自家 ROM 微調。Android 8 ~ 14 都還有人用
對策:選 top 5 機種覆蓋 80% 使用者 → 看你 Firebase Analytics 數據決定。
2. 網路情境
| 情境 | 怎麼測 |
|---|---|
| 慢速 4G | Charles Proxy / Network Link Conditioner |
| 切換 WiFi ↔ 4G | 中途切看 reconnect |
| 完全離線 | 飛航模式 |
| 不穩 / 高延遲 | Throttling |
3. 推播與背景
flowchart LR
A[App 在前景] --> B[Push 進來]
B --> C{Foreground<br>handler?}
C -->|有| Show[顯示自訂通知]
C -->|無| Default[OS 預設行為]
A2[App 在背景] --> B2[Push 進來]
B2 --> D[OS 通知區]
D --> Tap[使用者點]
Tap --> Open[開 App + deep link]
style Show fill:#10b981,color:#fff
style Default fill:#f59e0b,color:#fff
style Open fill:#06b6d4,color:#fff
Push notification 必測: - App 開啟時收到 - App 背景時收到 - App 完全 kill 時收到 - 點通知開到正確頁面(deep link) - 通知 payload 含特殊字元 / emoji - 連續多則通知 batching
4. 權限 flow
首次安裝 → 開 App → 觸發功能(拍照/位置/推播)→
彈權限 → 使用者允許 / 拒絕 / 「不要再問」→
不同分支 UX 都要測
權限拒絕後的 fallback path 常被忽略。
5. App lifecycle
- Foreground → Background(按 Home)
- Background → Foreground 回來
- Memory pressure 被 kill
- Cold start vs Warm start
- 來電中、收到簡訊
測試:跑流程到一半切走 / 切回,看狀態保留嗎?
自動化工具二選一:Appium vs Detox
flowchart TD
Choose{你的 App<br>類型?} --> RN[React Native]
Choose --> Native_[Native iOS/Android]
Choose --> Flutter[Flutter]
Choose --> Hybrid_[Hybrid]
RN --> Detox["Detox<br>(灰盒、最快)"]
Native_ --> XCUI[XCUITest + Espresso]
Native_ --> Appium_["Appium<br>(跨平台統一)"]
Flutter --> Patrol[Patrol / Flutter Driver]
Hybrid_ --> Appium2[Appium]
style Detox fill:#10b981,color:#fff
style Appium_ fill:#06b6d4,color:#fff
style Appium2 fill:#06b6d4,color:#fff
style Patrol fill:#a855f7,color:#fff
Appium — 通用之王
優點: - 跨平台同一份 code 跑 iOS + Android - 支援 Native / Cross / Hybrid - 業界標準、社群大、工作機會多 - 可寫 Python / Java / JS / Ruby
缺點: - 慢(每步都要過 WebDriver protocol) - Selector 不穩定(要用 Accessibility ID) - iOS 跑特別慢 - 環境配置複雜(Xcode + Android SDK + Node + Java...)
Detox — React Native 神器
優點: - 灰盒測試(跟 app process 同步) - 比 Appium 快 5-10 倍 - Auto-sync — 不用自己 wait - React Native 一等公民
缺點: - 只支援 RN - iOS Simulator + Android Emulator only(不能跑真機) - 學習曲線比 Appium 陡
我的選擇規則
| 你的情境 | 選 |
|---|---|
| RN App + 內部團隊 | Detox(速度、穩定) |
| Native App + 兩位專家分 iOS/Android | XCUITest + Espresso |
| 中小團隊、要跨平台共用 | Appium |
| Flutter | Patrol 或 Flutter Driver |
| Hybrid / WebView | Appium(其中 webview 部分用 Selenium) |
Appium 快速體驗
環境
brew install node android-platform-tools
npm install -g appium
appium driver install xcuitest
appium driver install uiautomator2
第一支 test(Python)
from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
options = UiAutomator2Options()
options.platform_name = 'Android'
options.device_name = 'Pixel_8_API_34'
options.app = '/path/to/app.apk'
options.automation_name = 'UiAutomator2'
driver = webdriver.Remote('http://localhost:4723', options=options)
# 選元素優先順序:
# 1. Accessibility ID (跨平台、最穩)
login_btn = driver.find_element(AppiumBy.ACCESSIBILITY_ID, 'login_button')
login_btn.click()
# 2. ID (Android resource-id / iOS name)
email = driver.find_element(AppiumBy.ID, 'email_input')
email.send_keys('[email protected]')
# 3. XPath (最後手段)
# 不要常用 - 慢、脆弱
跑
# 開 Appium server
appium
# 另一個 terminal 跑 test
python tests/test_login.py
Detox 快速體驗(RN)
// e2e/login.test.js
describe('Login', () => {
beforeEach(async () => {
await device.reloadReactNative();
});
it('logs in successfully', async () => {
await element(by.id('email-input')).typeText('[email protected]');
await element(by.id('password-input')).typeText('Pass@123');
await element(by.id('login-button')).tap();
await expect(element(by.text('Welcome'))).toBeVisible();
});
});
跑:
detox build -c ios.sim.debug
detox test -c ios.sim.debug
跨平台 selector 策略
flowchart LR
Test[Test code] --> Strategy{選 selector}
Strategy --> AID["accessibilityId<br>(跨平台統一)"]
Strategy --> IOS[iOS predicate<br>or name]
Strategy --> AND[Android resource-id<br>or content-desc]
AID --> Best["⭐ 推薦<br>同一個 ID 跨平台"]
IOS --> P2[要兩套 selector]
AND --> P3[要兩套 selector]
style Best fill:#10b981,color:#fff
style P2 fill:#f59e0b,color:#fff
style P3 fill:#f59e0b,color:#fff
團隊規範:
- 跟 dev 約定每個元素加
accessibilityID="meaningful-name" - 命名規則:
<screen>-<element>-<action>,例如login-email-input - Avoid 用 visible text(i18n 換語言就壞)
Device Farm — 雲端真機
本機跑 Simulator / Emulator 不夠真實。真機很重要。
| 服務 | 強項 | 弱項 |
|---|---|---|
| BrowserStack | UI 好、設備多 | 貴 |
| Sauce Labs | 老牌、企業 | 貴 |
| AWS Device Farm | 跟 AWS 整合 | UI 差 |
| Firebase Test Lab | Android 強、Google 親兒子 | iOS 少 |
| LambdaTest | 便宜 | 設備新但社群小 |
| 自架(OpenSTF) | 完全可控 | 維護成本 |
新團隊推薦:BrowserStack 或 Firebase Test Lab。設備數量、穩定度最值得。
環境準備陷阱
iOS
- Mac 必備(Xcode 只能 Mac 跑)
- Apple Developer 帳號 ($99/yr) 真機 sign
- Simulator 跟真機 behavior 不同(Push、Touch ID)
- 每個 Xcode 升級可能打死 Appium driver
Android
- Windows / Mac / Linux 都可
- Emulator 慢、最好真機
- 不同 OEM 行為不同(華為跟 Samsung 行為差異大)
- ADB 偶爾掛掉要重啟
CI 整合 — 痛點最深
flowchart LR
Push[Push code] --> Build[Build App]
Build --> Lint[Lint / Unit]
Lint --> SimE2E[Simulator E2E<br>每 PR]
SimE2E --> Nightly[真機 E2E<br>每晚]
Nightly --> Bs[BrowserStack<br>多裝置]
style SimE2E fill:#06b6d4,color:#fff
style Nightly fill:#a855f7,color:#fff
style Bs fill:#10b981,color:#fff
節奏: - PR:Simulator/Emulator E2E(10-15 分鐘) - Nightly:真機 device farm(多裝置) - Release:全裝置 + 多 OS 版本
不要 PR 跑真機 — 太慢、會被罵。
反模式
- 本機過就 push — 你 Pixel 8 過、別人 Samsung S20 爆
- 跑 release build 才測 — Release / Debug 行為不同,提早遇到
- 不測權限 fallback — 拒絕後 app crash 上 store 退
- 不測 deep link — push / 廣告連進來 crash
- 忘了測直 / 橫排 — 旋轉 90 度爆 layout
- iOS only / Android only 心態 — 必須兩平台都會
- 不測網路差 — 高鐵上一定壞
QA 跨進 Mobile 的學習路線
flowchart TD
Start[會 Web QA] --> S1["1) 學 Android Studio<br>+ Xcode 基礎"]
S1 --> S2["2) 學 ADB 指令"]
S2 --> S3["3) 手動測一個 App<br>找 20 個 bug"]
S3 --> S4["4) Appium / Detox 二選一"]
S4 --> S5["5) CI 整合"]
S5 --> S6["6) Device farm 上雲"]
S6 --> Pro[Mobile QA 中階]
style Pro fill:#10b981,color:#fff
時程:3-6 個月。
工具速查
| 工具 | 用途 |
|---|---|
| Appium Inspector | 看 UI element tree |
| Charles Proxy | 攔網路流量、改 response |
| Stetho | Android Chrome DevTools |
| Reactotron | React Native debugger |
| Flipper | RN / Native 通用 debugger |
| adb logcat | Android log |
| Xcode Console | iOS log |
| TestFlight | iOS beta 派發 |
| Firebase App Distribution | Android beta 派發 |
最後
Mobile QA 痛苦但薪資 / 職缺都比純 web 多。學完之後你不只是 web QA、是「跨平台 QA 工程師」— 職涯路寬一倍。從手動測一個 App 開始,下一篇進自動化選 Appium 或 Detox。