detox-skill について
React NativeアプリをテストするQAエンジニア向けのDetoxガイドです。要素の照合、操作、アサーション、デバイス制御に加え、AndroidとiOSのテストをローカルまたはTestMu AIクラウドで実行する方法を説明します。
主な機能
- 01Detoxのテスト導入と設定
- 02要素マッチャーとアサーション
- 03UI操作とデバイス制御
- 04AndroidとiOSのテストコマンド
- 05TestMu AIクラウド実行ガイド
- 06アンチパターンと応用資料
活用シーン
- 01React NativeのUIテスト作成
- 02要素の表示状態とテキストの確認
- 03デバイス状態の操作とジェスチャーの実行
- 04クラウド実行用モバイルテスト成果物の準備
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ lambdatest/ agent-skills --skill detox-skillインストール後、Claude Code で/detox-skillを入力して読み込まれたか確認します。
同梱ファイルreference/advanced-patterns.mdreference/playbook.md
詳細
Detox 自動化スキル
Detox を専門とするシニア React Native QA エンジニアです。
基本パターン
基本的なテスト
describe('Login', () => {
beforeAll(async () => {
await device.launchApp();
});
beforeEach(async () => {
await device.reloadReactNative();
});
it('should login with valid credentials', async () => {
await element(by.id('emailInput')).typeText('[email protected]');
await element(by.id('passwordInput')).typeText('password123');
await element(by.id('loginButton')).tap();
await expect(element(by.id('dashboardTitle'))).toBeVisible();
await expect(element(by.text('Welcome'))).toBeVisible();
});
it('should show error for invalid credentials', async () => {
await element(by.id('emailInput')).typeText('[email protected]');
await element(by.id('passwordInput')).typeText('wrong');
await element(by.id('loginButton')).tap();
await expect(element(by.id('errorMessage'))).toBeVisible();
});
});
マッチャー(要素の検索)
element(by.id('uniqueId')) // testID prop (best)
element(by.text('Login')) // Text content
element(by.label('Submit')) // Accessibility label
element(by.type('RCTTextInput')) // Native type
element(by.traits(['button'])) // iOS traits
// Combined
element(by.id('list').withDescendant(by.text('Item 1')))
element(by.id('item').withAncestor(by.id('list')))
// At index (multiple matches)
element(by.text('Delete')).atIndex(0)
アクション
await element(by.id('btn')).tap();
await element(by.id('btn')).longPress();
await element(by.id('btn')).multiTap(3);
await element(by.id('input')).typeText('hello');
await element(by.id('input')).replaceText('new text');
await element(by.id('input')).clearText();
await element(by.id('scrollView')).scroll(200, 'down');
await element(by.id('scrollView')).scrollTo('bottom');
await element(by.id('item')).swipe('left', 'fast');
await element(by.id('input')).tapReturnKey();
アサーション
await expect(element(by.id('title'))).toBeVisible();
await expect(element(by.id('title'))).not.toBeVisible();
await expect(element(by.id('title'))).toExist();
await expect(element(by.id('title'))).toHaveText('Welcome');
await expect(element(by.id('toggle'))).toHaveToggleValue(true);
await expect(element(by.id('input'))).toHaveValue('hello');
デバイス制御
await device.launchApp({ newInstance: true });
await device.reloadReactNative();
await device.sendToHome();
await device.terminateApp();
await device.installApp();
await device.shake(); // Shake gesture
await device.setLocation(37.7749, -122.4194); // GPS
await device.setURLBlacklist(['.*cdn.example.*']); // Block URLs
アンチパターン
| 悪い例 | 良い例 | 理由 |
|---|---|---|
あらゆる場所でwaitFor().withTimeout()を使う | Detox の自動同期を信頼する | Detox は自動的に待機します |
コンポーネントにtestIDがない | testIDプロパティを追加する | 安定したセレクターを使用できます |
すべてのテストでdevice.launchApp()を使う | device.reloadReactNative()を使う | より高速です |
| 手動で遅延を入れる | Detox の同期機能を使う | 待機機能が組み込まれています |
.detoxrc.js の設定
module.exports = {
testRunner: { args: { config: 'e2e/jest.config.js' } },
apps: {
'ios.debug': {
type: 'ios.app',
binaryPath: 'ios/build/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 16' } },
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' },
},
};
TestMu AI でのクラウド実行
Detox はネイティブのテストランナー(Android 用の Espresso、iOS 用の XCUITest)を生成します。ビルド済みアーティファクトをアップロードし、フレームワークビルドを実行して、TestMu AI クラウドで実行します。
Android(Espresso)のクラウド実行
# 1. Build the app and test APKs
detox build --configuration android.emu.debug
# 2. Upload app APK
curl -u "$LT_USERNAME:$LT_ACCESS_KEY" \
-X POST "https://manual-api.lambdatest.com/app/upload/realDevice" \
-F "appFile=@android/app/build/outputs/apk/debug/app-debug.apk" \
-F "name=DetoxApp"
# Response: {"app_url": "lt://APP_ID", ...}
# 3. Upload test APK
curl -u "$LT_USERNAME:$LT_ACCESS_KEY" \
-X POST "https://manual-api.lambdatest.com/app/upload/realDevice" \
-F "appFile=@android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk" \
-F "name=DetoxTestSuite"
# Response: {"app_url": "lt://TEST_SUITE_ID", ...}
# 4. Trigger Espresso build
curl -X POST "https://mobile-api.lambdatest.com/framework/v1/espresso/build" \
-H "Authorization: Basic <BASE64_AUTH>" \
-H "Content-Type: application/json" \
-d '{
"app": "lt://APP_ID",
"testSuite": "lt://TEST_SUITE_ID",
"device": ["Galaxy S21 5G-12", "Pixel 6-12"],
"build": "Detox-Android",
"deviceLog": true,
"video": true
}'
iOS(XCUITest)のクラウド実行
# 1. Build the iOS app and test runner
detox build --configuration ios.sim.release
# 2. Create .ipa from the build artifacts, then upload
curl -u "$LT_USERNAME:$LT_ACCESS_KEY" \
-X POST "https://manual-api.lambdatest.com/app/upload/realDevice" \
-F "[email protected]" -F "name=DetoxApp"
# 3. Upload test runner .ipa
curl -u "$LT_USERNAME:$LT_ACCESS_KEY" \
-X POST "https://manual-api.lambdatest.com/app/upload/realDevice" \
-F "[email protected]" -F "name=DetoxTestSuite"
# 4. Trigger XCUITest build
curl -X POST "https://mobile-api.lambdatest.com/framework/v1/xcui/build" \
-H "Authorization: Basic <BASE64_AUTH>" \
-H "Content-Type: application/json" \
-d '{
"app": "lt://APP_ID",
"testSuite": "lt://TEST_SUITE_ID",
"device": ["iPhone 14-16", "iPhone 13-15"],
"build": "Detox-iOS",
"devicelog": true,
"video": true
}'
クイックリファレンス
| タスク | コマンド |
|---|---|
| iOS をビルド | detox build --configuration ios.sim.debug |
| iOS のテスト | detox test --configuration ios.sim.debug |
| Android をビルド | detox build --configuration android.emu.debug |
| Android のテスト | detox test --configuration android.emu.debug |
| 特定のテスト | detox test --configuration ios.sim.debug e2e/login.test.js |
| 動画を録画 | detox test --record-videos all |
| スクリーンショットを撮影 | await device.takeScreenshot('login-page') |
セットアップ
npm install detox --save-dev
npm install jest jest-circus --save-dev
detox init
React Native コンポーネントのセットアップ
// Add testID to components for Detox
<TextInput testID="emailInput" placeholder="Email" />
<TextInput testID="passwordInput" placeholder="Password" secureTextEntry />
<Button testID="loginButton" title="Login" onPress={handleLogin} />
<Text testID="errorMessage">{error}</Text>
発展的なパターン
発展的なパターン、デバッグガイド、CI/CD 連携、ベストプラクティスについては、
reference/playbook.mdを参照してください。
コメント
Markdown 対応