Detox 完整入門 — React Native 自動化神器 從 0 到 CI 整合
Detox 完整指南。React Native E2E 框架、灰盒測試原理、Sync 機制、Page Object 適配、Mocking、CI 整合(GitHub Actions + Firebase Test Lab)、跟 Appium 對比。
💡 本文原刊於 qa.9niche.com,2026-08 併入 9niche.com 懶人包,內容照原文完整搬遷。
目錄
1. 前言
「我們 RN app 用 Appium 跑、每個 case 20 秒」 → 你浪費了 80% 時間。Detox 為 RN 量身打造、灰盒測試、自動 sync app idle,同 case 跑 4 秒。這篇給你 0 到 CI 完整 setup。
2. 為什麼 Detox 比 Appium 快
核心:Detox 知道 RN「現在閒了」、立刻執行下一動作;Appium 只能 sleep + retry。
3. 30 分鐘 0 到綠 CI
Setup
# 1. Detox CLI
npm install -g detox-cli
# 2. 專案內裝
cd MyRNApp
npm install -D detox jest @types/jest
# 3. Init
detox init
會建:
.detoxrc.js— 設定檔e2e/jest.config.js— Jest 配置e2e/starter.test.js— 範例 test
.detoxrc.js 配置
module.exports = {
apps: {
'ios.debug': {
type: 'ios.app',
binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
build: 'xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build',
},
'android.debug': {
type: 'android.apk',
binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk',
build: 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug',
reversePorts: [8081],
},
},
devices: {
simulator: { type: 'ios.simulator', device: { type: 'iPhone 15' } },
emulator: { type: 'android.emulator', device: { avdName: 'Pixel_7_API_34' } },
},
configurations: {
'ios.sim.debug': { device: 'simulator', app: 'ios.debug' },
'android.emu.debug': { device: 'emulator', app: 'android.debug' },
},
};
第一個 test
// e2e/login.test.js
describe('Login flow', () => {
beforeAll(async () => {
await device.launchApp();
});
beforeEach(async () => {
await device.reloadReactNative();
});
it('登入成功跳轉 Home', async () => {
await element(by.id('email-input')).typeText('[email protected]');
await element(by.id('password-input')).typeText('Test@123');
await element(by.id('login-button')).tap();
await expect(element(by.text('Welcome'))).toBeVisible();
});
it('錯密碼顯示錯誤', async () => {
await element(by.id('email-input')).typeText('[email protected]');
await element(by.id('password-input')).typeText('wrong');
await element(by.id('login-button')).tap();
await expect(element(by.text('帳號或密碼錯誤'))).toBeVisible();
});
});
跑
# Build
detox build -c ios.sim.debug
# Test
detox test -c ios.sim.debug
# Android
detox build -c android.emu.debug
detox test -c android.emu.debug
4. Selector 哲學
前端需要配合加 testID:
<TouchableOpacity testID="login-button" onPress={login}>
<Text>登入</Text>
</TouchableOpacity>
<TextInput testID="email-input" value={email} onChangeText={setEmail} />
5. Auto-sync — Detox 的殺手鐧
不用 sleep、不用 waitFor。Detox 自動等到 app idle 才動。
例外:手動 sync 控制
// 暫停 sync(要測 loading state)
await device.disableSynchronization();
await element(by.id('submit')).tap();
await expect(element(by.id('spinner'))).toBeVisible();
await device.enableSynchronization();
6. Mocking — Network / Module
Mock 網路請求
// 用 reactotron-mocks 或自寫 middleware
beforeEach(async () => {
await device.launchApp({
launchArgs: { MOCK_API: 'true' },
});
});
// app 端
if (Config.MOCK_API) {
api.interceptors.response.use(req => mockResponses[req.url]);
}
Mock Native Module
// e2e/mocks/Permissions.js
export default {
request: () => Promise.resolve('granted'),
check: () => Promise.resolve('granted'),
};
7. Page Object Model 適配
// e2e/pages/LoginPage.js
class LoginPage {
emailInput = () => element(by.id('email-input'));
passwordInput = () => element(by.id('password-input'));
submitBtn = () => element(by.id('login-button'));
errorText = () => element(by.text('帳號或密碼錯誤'));
async login(email, password) {
await this.emailInput().typeText(email);
await this.passwordInput().typeText(password);
await this.submitBtn().tap();
}
}
export default new LoginPage();
// e2e/login.test.js
import LoginPage from './pages/LoginPage';
it('login success', async () => {
await LoginPage.login('[email protected]', 'Test@123');
await expect(element(by.text('Welcome'))).toBeVisible();
});
8. CI 整合 — GitHub Actions
iOS(macOS runner)
name: E2E iOS
on: [pull_request]
jobs:
ios:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- name: Pod install
run: cd ios && pod install
- name: Build Detox
run: detox build -c ios.sim.debug
- name: Run Detox
run: detox test -c ios.sim.debug --headless --record-logs all
- if: failure()
uses: actions/upload-artifact@v4
with:
name: detox-artifacts-ios
path: artifacts/
Android(Ubuntu runner)
android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- uses: actions/setup-java@v4
with: { distribution: 'temurin', java-version: '17' }
- run: npm ci
- name: AVD cache
uses: actions/cache@v4
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-pixel-7-api-34
- name: Build Detox
run: detox build -c android.emu.debug
- name: Run Detox in emulator
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 34
target: google_apis
arch: x86_64
profile: pixel_7
script: detox test -c android.emu.debug --headless
9. 跟 Firebase Test Lab 整合
Detox build 出來的 APK 可以上傳 FTL 跑跨真機:
# 1. Build instrumented APK + test APK
detox build -c android.emu.debug
# 2. 上傳 FTL
gcloud firebase test android run \
--type instrumentation \
--app android/app/build/outputs/apk/debug/app-debug.apk \
--test android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk \
--device model=Pixel7,version=34 \
--device model=SamsungGalaxyS22,version=33
local Detox 快 debug + FTL 跨真機驗證。延伸:Firebase Test Lab 完整指南
10. 常見 7 個坑
1. iOS Simulator 抓不到 testID
原因:accessibility 沒 enable。 解:iOS Settings → Accessibility → 開 VoiceOver 一次(之後可關)。
2. typeText 中文字輸不進去
原因:Detox 用 native keyboard、不支援 IME。 解:用 replaceText 取代 typeText。
await element(by.id('input')).replaceText('你好世界');
3. 動畫導致 sync timeout
原因:CSS animation 或 native animation 永遠不 idle。 解:手動 device.disableSynchronization() 或在 dev mode 關閉動畫。
4. WebView 內容測不到
原因:Detox 不直接支援 WebView。 解:用 web fallback 或 switch context(有限支援)。
5. CI emulator 啟動慢
原因:cold boot 每次 5+ 分鐘。 解:cache AVD(如上面 yml)+ 用 quick boot snapshot。
6. Flaky test 集中在某 modal
原因:modal animation 沒結束就互動。 解:用 waitFor(element).toBeVisible().withTimeout(5000) 顯式等。
7. Memory leak 跑 100 個 test 後 OOM
原因:每 test 沒 reload。 解:beforeEach: await device.reloadReactNative();
11. Detox vs Appium 對比
| 維度 | Detox | Appium |
|---|---|---|
| 速度 | ⚡ 快 5-10x | 慢 |
| 支援框架 | RN(主)/ Native | 任何 |
| 跨平台 code | 同一份 | 同一份(但細節差) |
| Auto-sync | ✓ 內建 | ✗ 要手動 wait |
| 真機 | iOS 部分、Android 需設定 | 完整 |
| WebView | 弱 | 強 |
| 學習曲線 | 中 | 高(環境設定) |
| 社群 | 中(RN 用戶) | 大(業界標準) |
12. 反模式
13. 給 RN QA 的 5 句
- RN 就選 Detox、別碰 Appium 浪費生命
- 跟 dev 配合 testID = 一勞永逸
- Sync 是神功、別輕易關
- Local Detox + FTL = 完整 Android 覆蓋
- CI 一定 cache AVD、不然每次 5 分鐘 boot
14. 最後
Detox 是 React Native QA 的最佳投資。從 setup 到第一個綠 CI 30 分鐘、之後每個 test 比 Appium 省 70% 時間。一年累積省下的時間夠你深入學 5 個新主題。
延伸:
相關連結
POM 完整指南。為什麼用、怎麼拆 class、Playwright 實作範例、Component Object 進階、反模式。附類別關係圖與重構流程。
Firebase Test Lab 完整指南。3 種測試類型、跨真機矩陣、Robo auto-explorer、CI 整合(gcloud + GitHub Actions)、vs BrowserStack 對比、iOS 限制、成本控制、常見坑。
Mobile QA 完整入門。原生 / 跨平台 / hybrid 三種 app 差異、Appium vs Detox 取捨、跟 Web testing 不同的坑、雲端 device farm 選擇。
相關懶人包
2026 QA 趨勢實戰:我看到的 5 個轉變(AI、Shift-Left、Observability)
從手動 QA 到 AI 輔助、從測試金字塔到測試獎盃。這篇分享我這 10+ 年看 QA 從「測完才知道」到「shift-left + AI」的真實觀察。
2026 QA 面試的 AI 題 — 12 題 + 答題框架(面試官想聽什麼)
2026 QA 面試新增一整類「你怎麼用 AI」的問題。這篇整理 12 個高頻 AI 面試題、每題附面試官真正想聽的點與答題框架,從「你用過哪些 AI 工具」到「AI 生的 test 怎麼信任」。
AI / LLM 功能 Spec Review — 幻覺 / 評估 / 成本 / 法遵 8 個必問
AI 功能 spec review 完整指南。LLM 不確定性處理、評估指標、Prompt versioning、成本控制、安全護欄、法遵(EU AI Act / GDPR)、Fallback、人工 review 流程。
一般聲明
本站提供之資訊僅供參考,不保證其完整性與正確性。使用者應自行判斷資訊之適用性。