5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIがiOS/Androidシミュレータを自律操作!?E2Eテストの自動生成・自動修復を試してみた

Last updated at Posted at 2025-12-20

この記事について

項目 内容
対象読者 モバイルアプリのE2Eテストに興味がある人、生成AIを活用したい人
前提知識 以前の記事(Playwright Agents編)を読んでいるとより理解しやすい
検証環境 macOS 15.7 / Xcode 16.4 / adb 1.0.41 / Node.js 24.x / mobile-mcp 0.0.38 / Kiro CLI 1.22.0

✅ この記事を読むと

  • mobile-mcpの導入方法とAIエージェントからiOS/Androidを操作する方法がわかる
  • AIによるモバイルアプリのテスト計画・テストコード自動生成を試せるようになる

※ 本記事ではKiro CLIを使用していますが、mobile-mcpはMCP対応のあらゆるAIエージェント(Claude Desktop、Cursor、VS Code Copilotなど)で利用可能です。

📱 以前の記事のおさらい

以前の記事では、Playwright AgentsをKiro CLIで動かす方法を紹介しました。

Playwright Agentsは、AIエージェントがブラウザを操作してE2Eテストを実行できるツールです。

  • 🌐 Webページを自動探索
  • 📝 テスト計画を自動生成
  • 🧪 Playwrightのテストコードを生成
  • 🩹 失敗したテストを自動修復

「これ、モバイルアプリでもできたら最高じゃない?」

そう思って調べてみたら、ありました。mobile-mcpです!

🤖 mobile-mcpとは

mobile-mcpは、AIエージェントがiOS/Androidのシミュレータ・エミュレータ・実機を操作できるようにするMCPサーバーです。

📺 動作イメージはGitHubのデモ動画を参照してください。

主な機能

カテゴリ ツール 説明
デバイス管理 mobile_list_available_devices 利用可能なデバイス一覧
mobile_get_screen_size 画面サイズ取得
アプリ管理 mobile_launch_app アプリ起動
mobile_terminate_app アプリ終了
画面操作 mobile_take_screenshot スクリーンショット
mobile_list_elements_on_screen UI要素一覧取得
mobile_click_on_screen_at_coordinates 座標タップ
mobile_swipe_on_screen スワイプ
入力 mobile_type_keys テキスト入力
mobile_press_button ボタン押下(HOME, BACKなど)

iOS/Androidのシミュレータ・エミュレータ・実機すべてに対応しています。

⚙️ セットアップ

前提条件

mobile-mcpを使うには、以下のツールが必要です。

ツール 用途 インストール方法
Node.js v22+ MCPサーバー実行 nodejs.org
Xcode Command Line Tools iOS自動化 xcode-select --install
Android SDK Platform-Tools Android自動化 developer.android.com

iOS固有の準備

1. mobilecliのインストール

mobile-mcpはiOSシミュレータの操作に内部でmobilecliを使用しています。このパッケージはoptionalDependenciesのため、環境によっては自動インストールされないことがあります。

npm install -g @mobilenext/mobilecli@latest

# インストール確認
npx @mobilenext/mobilecli --version
# mobilecli version 0.0.48 のように表示されればOK

mobilecliがインストールされていないと、タップ操作が「成功」と表示されても実際には画面に反映されません。

2. WebDriverAgent(WDA)の事前起動

mobilecliをインストールしても、初回はWDAが起動していないため、タップ操作が効かないことがあります。

mobile-mcpを使う前に、以下のコマンドでWDAを起動しておいてください。

# デバイスIDを確認
npx @mobilenext/mobilecli devices --platform ios --type simulator

# WDAを起動(ダミーのタップを実行)
npx @mobilenext/mobilecli io tap "0,0" --device <device-id>

このコマンドを実行すると、mobilecliがポート13001でWDAを自動起動します。一度起動すれば、シミュレータを再起動するまで有効です。

3. シミュレータの起動

# シミュレータ一覧
xcrun simctl list devices

# シミュレータ起動
xcrun simctl boot "iPhone 16"
open -a Simulator

手動でWDAを起動したい場合(トラブルシューティング用)

git clone --depth 1 https://github.com/appium/WebDriverAgent.git
cd WebDriverAgent
xcodebuild -project WebDriverAgent.xcodeproj \
  -scheme WebDriverAgentRunner \
  -destination 'platform=iOS Simulator,name=iPhone 16' test

手動起動したWDA(ポート8100)とmobilecliが起動するWDA(ポート13001)は別物です。mobile-mcpはmobilecli経由でWDAを使用するため、手動起動は通常不要です。

Android固有の準備

# エミュレータ一覧
emulator -list-avds

# エミュレータ起動
emulator -avd Pixel_7_API_34

MCPサーバーの設定

お使いのAIエージェントにmobile-mcpを追加します。

Kiro CLIの場合

~/.kiro/settings/mcp.json:

{
  "mcpServers": {
    "mobile-mcp": {
      "command": "npx",
      "args": ["-y", "@mobilenext/mobile-mcp@latest"],
      "timeout": 60000
    }
  }
}

Claude Desktop、Cursor、VS Code Copilotなど他のMCP対応ツールでも同様に設定できます。詳細はmobile-mcp Wikiを参照してください。

🎭 専門エージェントの作成

Playwright Agentsと同じ構成で、モバイルテスト用の専門エージェントを作成しました。

エージェント 役割
mobile-test-planner アプリを探索してテスト計画を作成
mobile-test-generator XCUITest/Espressoコードを生成
mobile-test-healer 失敗したテストを修復

エージェント定義ファイル

~/.kiro/agents/mobile-test-planner.json:

{
  "name": "mobile-test-planner",
  "description": "モバイルアプリの包括的なテスト計画を作成する専門エージェント",
  "prompt": "file://./prompts/mobile-test-planner.md",
  "mcpServers": {
    "mobile-mcp": {
      "command": "npx",
      "args": ["-y", "@mobilenext/mobile-mcp@latest"],
      "timeout": 120000
    }
  },
  "tools": ["*"],
  "allowedTools": ["fs_read", "fs_write", "@mobile-mcp"]
}

プロンプトファイル

~/.kiro/agents/prompts/mobile-test-planner.md:

You are an expert mobile app test planner with extensive experience 
in iOS and Android quality assurance.

You will:

1. **Connect and Explore**
   - Use `mobile_list_available_devices` to find available devices
   - Use `mobile_list_elements_on_screen` to explore the current screen
   - Use `mobile_click_on_screen_at_coordinates`, `mobile_swipe_on_screen` 
     to navigate and discover the interface

2. **Analyze User Flows**
   - Map out the primary user journeys
   - Identify critical paths through the application

3. **Design Comprehensive Scenarios**
   - Happy path scenarios
   - Edge cases and boundary conditions
   - Error handling and validation

4. **Structure Test Plans**
   Each scenario must include:
   - Clear, descriptive title
   - Target platform (iOS/Android/Both)
   - Detailed step-by-step instructions with element identifiers
   - Expected outcomes
   - Success criteria and failure conditions

5. **Create Documentation**
   Save your test plan as a markdown file.

mobile-test-generatormobile-test-healerも同様の構成で作成します。

~/.kiro/agents/prompts/mobile-test-generator.md:

You are an expert mobile test automation engineer.

You will:

1. **Read Test Plan**
   - Read the provided test plan markdown file
   - Understand the test scenarios and expected outcomes

2. **Generate Test Code**
   - For iOS: Generate XCUITest code in Swift
   - For Android: Generate Espresso code in Kotlin
   - Use the element identifiers from the test plan

3. **Best Practices**
   - Add proper wait conditions (waitForExistence, etc.)
   - Include meaningful assertions
   - Handle setup and teardown appropriately
   - Add comments explaining each test step

4. **Save the Code**
   - Save the generated test file to the specified output path

~/.kiro/agents/prompts/mobile-test-healer.md:

You are an expert mobile test debugger and healer.

You will:

1. **Analyze Failed Test**
   - Read the failing test code
   - Identify the problematic selectors or assertions

2. **Inspect Current UI**
   - Use `mobile_list_available_devices` to connect to the device
   - Use `mobile_launch_app` to launch the target app
   - Use `mobile_list_elements_on_screen` to get the actual UI hierarchy
   - Navigate to the relevant screen if needed

3. **Fix the Test**
   - Compare expected selectors with actual UI elements
   - Update selectors to match the real UI hierarchy
   - Add proper wait conditions if missing

4. **Save the Fixed Code**
   - Overwrite the original test file with the corrected code

🚀 実際に動かしてみた(iOS編)

テスト対象

iOSシミュレータの設定アプリ(com.apple.Preferences)で、3つのエージェント(Planner → Generator → Healer)のワークフローを検証しました。

Step 1: テスト計画の作成(Planner)

kiro-cli chat --agent mobile-test-planner --no-interactive \
  "iOSシミュレータに接続して、設定アプリの「一般」→「情報」画面への
   ナビゲーションをテストするテスト計画を作成してください。
   テスト計画は $(pwd)/tests/ios/settings-general-about.md に保存してください。"
📋 実行ログ(クリックで展開)
> iOSシミュレータに接続して、設定アプリを探索します。

Running tool mobile_list_available_devices (from mcp server: mobile-mcp)
- Completed in 0.5s

> iPhone 16シミュレータが利用可能です。設定アプリを起動します。

Running tool mobile_launch_app (from mcp server: mobile-mcp)
  { "device": "92103385-3E8D-4992-AA66-62A5617288CA",
    "packageName": "com.apple.Preferences" }
- Completed in 0.5s

Running tool mobile_list_elements_on_screen (from mcp server: mobile-mcp)
  { "device": "92103385-3E8D-4992-AA66-62A5617288CA" }
- Completed in 0.4s

> 設定アプリのメイン画面が表示されました。「一般」セルを探します。

Running tool mobile_click_on_screen_at_coordinates (from mcp server: mobile-mcp)
  { "device": "92103385-3E8D-4992-AA66-62A5617288CA",
    "x": 207, "y": 756 }
- Completed in 1.3s

> 「一般」画面に遷移しました。「情報」セルを探します。

Running tool mobile_list_elements_on_screen (from mcp server: mobile-mcp)
- Completed in 0.5s

Running tool mobile_click_on_screen_at_coordinates (from mcp server: mobile-mcp)
  { "x": 207, "y": 378 }
- Completed in 1.4s

> 「情報」画面が表示されました。テスト計画を作成します。

I'll create the following file: tests/ios/settings-general-about.md

Plannerが実際にシミュレータを操作して画面遷移を確認し、テスト計画を作成してくれました!

📋 生成されたテスト計画

# iOS設定アプリ テスト計画: 一般 → 情報 ナビゲーション

## テスト概要
- **対象アプリ**: 設定 (com.apple.Preferences)
- **対象画面**: 一般 → 情報
- **プラットフォーム**: iOS Simulator (iPhone 16)

## テストケース

### TC-001: 一般画面から情報画面への遷移と戻り

**前提条件**: 設定アプリが起動し、メイン画面が表示されている

| ステップ | 操作 | 要素 | 期待結果 |
|:---:|:---|:---|:---|
| 1 | 「一般」セルをタップ | cells["com.apple.settings.general"] @ (207, 756) | 一般画面に遷移 |
| 2 | 「情報」セルをタップ | cells["About"] @ (207, 378) | 情報画面に遷移 |
| 3 | 戻るボタンをタップ | buttons["一般"] | 一般画面に戻る |
| 4 | 戻るボタンをタップ | buttons["設定"] | メイン画面に戻る |

### TC-002: 情報画面のコンテンツ確認

**前提条件**: 情報画面が表示されている

| 確認項目 | 要素 | 期待結果 |
|:---|:---|:---|
| デバイス名 | staticTexts["NAME_CELL_ID"] | デバイス名が表示される |
| iOSバージョン | staticTexts containing "iOS" | バージョン情報が表示される |

ポイント: 実際のUI要素のidentifierと座標が記録されている!

Step 2: テストコードの生成(Generator)

kiro-cli chat --agent mobile-test-generator --no-interactive \
  "$(pwd)/tests/ios/settings-general-about.md のテスト計画に基づいて、
   XCUITestコードを生成してください。
   出力先: $(pwd)/tests/ios/SettingsAboutTests.swift"

📋 生成されたXCUITestコード

import XCTest

final class SettingsAboutTests: XCTestCase {
    var app: XCUIApplication!

    override func setUpWithError() throws {
        continueAfterFailure = false
        app = XCUIApplication(bundleIdentifier: "com.apple.Preferences")
        app.launch()
    }

    /// TC-001: 一般画面から情報画面への遷移と戻り
    func testNavigateToAboutAndBack() throws {
        // Step 1: 「一般」セルをタップ
        let generalCell = app.cells["com.apple.settings.general"]
        XCTAssertTrue(generalCell.waitForExistence(timeout: 5), "一般セルが見つかりません")
        generalCell.tap()

        // Step 2: 「情報」セルをタップ
        let aboutCell = app.cells["About"]
        XCTAssertTrue(aboutCell.waitForExistence(timeout: 5), "情報セルが見つかりません")
        aboutCell.tap()

        // Step 3: 戻るボタンをタップ(一般画面へ)
        let backToGeneral = app.buttons["一般"]
        XCTAssertTrue(backToGeneral.waitForExistence(timeout: 5))
        backToGeneral.tap()

        // Step 4: 戻るボタンをタップ(メイン画面へ)
        let backToSettings = app.buttons["設定"]
        XCTAssertTrue(backToSettings.waitForExistence(timeout: 5))
        backToSettings.tap()

        // メイン画面に戻ったことを確認
        XCTAssertTrue(generalCell.waitForExistence(timeout: 5))
    }

    /// TC-002: 情報画面のコンテンツ確認
    func testAboutScreenContent() throws {
        // 情報画面まで遷移
        app.cells["com.apple.settings.general"].tap()
        app.cells["About"].tap()

        // デバイス名が表示されていることを確認
        let deviceName = app.staticTexts["NAME_CELL_ID"]
        XCTAssertTrue(deviceName.waitForExistence(timeout: 5), "デバイス名が見つかりません")
    }
}

Generatorはテスト計画のidentifierをそのまま使用して、正確なXCUITestコードを生成してくれました!

Step 3: 壊れたテストの修復(Healer)

テストが失敗した場合は、mobile-test-healerエージェントが原因を調査して修正案を提示してくれます。

実際にiOSシミュレータで検証した結果を紹介します!

わざと壊れたテストを作成

Healerの動作を検証するため、手動で間違ったセレクタを使ったテストコードを作成しました。

// BrokenSettingsTests.swift - わざと壊れたテスト
import XCTest

final class BrokenSettingsTests: XCTestCase {
    var app: XCUIApplication!

    override func setUpWithError() throws {
        continueAfterFailure = false
        app = XCUIApplication(bundleIdentifier: "com.apple.Preferences")
        app.launch()
    }

    // 壊れたテスト: 間違ったセレクタを使用
    func testNavigateToAbout() throws {
        // 間違い1: buttonsではなくcellsを使うべき
        let generalButton = app.buttons["GeneralSettings"]
        XCTAssertTrue(generalButton.exists)
        generalButton.tap()

        // 間違い2: 存在しないセレクタ
        let aboutButton = app.buttons["AboutPhone"]
        XCTAssertTrue(aboutButton.exists)
        aboutButton.tap()

        // 間違い3: waitForExistenceを使っていない
        XCTAssertTrue(app.staticTexts["DeviceName"].exists)
    }
}

Healerで修復

kiro-cli chat --agent mobile-test-healer --no-interactive \
  "$(pwd)/tests/ios/BrokenSettingsTests.swift のテストが失敗しています。
   iOSシミュレータに接続して現在のUI状態を確認し、
   正しいセレクタに修正してください。
   修正後のコードは同じファイルに上書き保存してください。"
📋 実行ログ(クリックで展開)
> まず、失敗しているテストコードを確認し、利用可能なデバイスを調べます。

Reading file: BrokenSettingsTests.swift, all lines
Running tool mobile_list_available_devices (from mcp server: mobile-mcp)
- Completed in 0.481s

> iPhone 16シミュレータが利用可能です。設定アプリを起動してUI要素を確認します。

Running tool mobile_launch_app (from mcp server: mobile-mcp)
Running tool mobile_list_elements_on_screen (from mcp server: mobile-mcp)

> 「一般」セルをタップして画面遷移を確認します。

Running tool mobile_click_on_screen_at_coordinates (from mcp server: mobile-mcp)
Running tool mobile_list_elements_on_screen (from mcp server: mobile-mcp)

> 「情報」画面が表示されました。デバイス名の identifier は NAME_CELL_ID です。

## 調査結果まとめ

| 元のセレクタ | 正しいセレクタ | 要素タイプ |
|-------------|---------------|-----------|
| app.buttons["GeneralSettings"] | app.cells["com.apple.settings.general"] | Cell |
| app.buttons["AboutPhone"] | app.cells["About"] | Cell |
| app.staticTexts["DeviceName"] | app.staticTexts["NAME_CELL_ID"] | StaticText |

修正結果

Healerが実際にシミュレータに接続してUI階層を確認し、自動的にコードを修正してくれました!

問題箇所 修正前 修正後
一般セル app.buttons["GeneralSettings"] app.cells["com.apple.settings.general"]
情報セル app.buttons["AboutPhone"] app.cells["About"]
デバイス名 app.staticTexts["DeviceName"] app.staticTexts["NAME_CELL_ID"]
待機処理 .exists .waitForExistence(timeout: 5)

修正後のコード:

import XCTest

final class BrokenSettingsTests: XCTestCase {
    var app: XCUIApplication!

    override func setUpWithError() throws {
        continueAfterFailure = false
        app = XCUIApplication(bundleIdentifier: "com.apple.Preferences")
        app.launch()
    }

    func testNavigateToAbout() throws {
        let generalCell = app.cells["com.apple.settings.general"]
        XCTAssertTrue(generalCell.waitForExistence(timeout: 5))
        generalCell.tap()

        let aboutCell = app.cells["About"]
        XCTAssertTrue(aboutCell.waitForExistence(timeout: 5))
        aboutCell.tap()

        XCTAssertTrue(app.staticTexts["NAME_CELL_ID"].waitForExistence(timeout: 5))
    }
}

Healerが行った修正のポイント

  1. 要素タイプの修正: buttonscells(設定アプリのメニュー項目はセル)
  2. 正しいidentifierの特定: シミュレータのUI階層から実際のidentifierを取得
  3. waitForExistenceの追加: 非同期読み込みに対応するためタイムアウト付きの待機を追加

⚠️ 注意点:画面遷移検出の無限ループ

検証中に発見した問題として、AIエージェントが画面遷移を検出できずに無限ループに陥るケースがありました。

Running tool tap (from mcp server: mobile-mcp)
  { "x": 300, "y": 372 }
- Completed in 0.185s

Running tool get_ui_hierarchy (from mcp server: mobile-mcp)
  {}
- Completed in 0.247s

Running tool tap (from mcp server: mobile-mcp)
  { "x": 300, "y": 372 }  ← 同じ座標を繰り返しタップ
- Completed in 0.163s
...(延々と続く)

原因: get_ui_hierarchyの結果が変化しない(または取得に失敗する)ため、AIが「画面が遷移していない」と判断し、同じ操作を繰り返してしまう。

対策:

  1. タイムアウト設定: エージェント定義で適切なタイムアウトを設定
  2. 明確な指示: 「3回試行しても遷移しない場合は次のステップへ進む」などの指示を追加
  3. 手動介入: 無限ループを検知したらプロセスを停止し、手動でテスト計画を作成

この問題は、mobile-mcpとAIエージェントの連携における課題の一つです。今後のアップデートで改善されることを期待しています。

🤖 Androidでも試してみた

Androidエミュレータでも同じワークフロー(Planner → Generator → Healer)を検証しました。iOS編と同様に動作することを確認できました。

iOS版との主な違い

項目 iOS Android
対象アプリ 設定アプリ(com.apple.Preferences) 設定アプリ(com.android.settings)
要素識別子 identifier(例: com.apple.settings.general resource-id / text(例: android:id/title
生成コード XCUITest (Swift) Espresso (Kotlin)
UI階層取得 環境によりエラーになることがある 安定して取得できた

実行例

# Androidエミュレータを起動
~/Library/Android/sdk/emulator/emulator -avd Medium_Phone_API_36.1 &

# テスト計画を作成
kiro-cli chat --agent mobile-test-planner --no-interactive \
  "Androidエミュレータに接続して、設定アプリ(com.android.settings)の
   「ネットワークとインターネット」画面へのナビゲーションをテストする
   テスト計画を作成してください。
   テスト計画は $(pwd)/tests/android/settings-network.md に保存してください。"
📋 実行ログ(クリックで展開)
> まず、利用可能なAndroidエミュレータを確認します。

Running tool mobile_list_available_devices (from mcp server: mobile-mcp)
- Completed in 0.329s

> Androidエミュレータ(emulator-5554)が利用可能です。設定アプリを起動します。

Running tool mobile_launch_app (from mcp server: mobile-mcp)
  { "device": "emulator-5554",
    "packageName": "com.android.settings" }
- Completed in 1.147s

Running tool mobile_list_elements_on_screen (from mcp server: mobile-mcp)
  { "device": "emulator-5554" }
- Completed in 2.523s

> 設定アプリのメイン画面が表示されています。
  「Network & internet」が座標(221, 586)にあります。これをタップして遷移を確認します。

Running tool mobile_click_on_screen_at_coordinates (from mcp server: mobile-mcp)
  { "device": "emulator-5554", "x": 221, "y": 586 }
- Completed in 0.141s

Running tool mobile_list_elements_on_screen (from mcp server: mobile-mcp)
- Completed in 2.490s

> 「Network & internet」画面に正常に遷移しました。

生成されたEspressoコード

@RunWith(AndroidJUnit4::class)
class SettingsNetworkTests {

    @get:Rule
    val activityRule = ActivityScenarioRule(SettingsActivity::class.java)

    @Test
    fun testNavigateToNetworkAndInternet() {
        onView(withText("Network & internet")).perform(click())
        onView(withText("Internet")).check(matches(isDisplayed()))
        onView(withText("SIMs")).check(matches(isDisplayed()))
    }

    @Test
    fun testNavigateUpToMainScreen() {
        onView(withText("Network & internet")).perform(click())
        onView(withContentDescription("Navigate up")).perform(click())
        onView(withText("Network & internet")).check(matches(isDisplayed()))
    }
}

Healerによる修復もiOS版と同様に動作し、間違ったセレクタ(withText("NetworkSettings")など)を正しいセレクタ(withText("Network & internet"))に自動修正してくれました。

😵 ハマったポイント

検証中に遭遇した問題と解決策をまとめます。

問題1: mobilecliがインストールされていない

症状: タップ操作が「成功」と表示されるが、実際には画面に反映されない

解決策: mobilecliを明示的にインストール

npm install -g @mobilenext/mobilecli@latest

問題2: WDAが起動していない

症状: 初回実行時にタップが効かない

解決策: 事前にmobilecliでWDAを起動

npx @mobilenext/mobilecli io tap "0,0" --device <device-id>

📝 まとめ

「モバイルアプリでもPlaywright Agentsみたいなことができたら便利だろうな」と思って調べてみたところ、iOS/Androidのシミュレータを操作できるmobile-mcpを発見!

「Playwright用のプロンプトをベースにいけるかな?」と思い、ツール名やコード生成の指示を調整してみたら、案外すんなり動かすことができました。

AIがアプリを探索してテスト計画を作成し、その計画からテストコードを生成。さらにシミュレータで確認しながら壊れたテストを自動修正してくれます。

自分は指示を出しただけなのに、シミュレータが勝手にタップしたりスワイプしたりしているのを見ると、「おお、動いてる!」とちょっと感動しました。

3つのエージェントの連携フロー

  1. Planner: デバイスに接続 → アプリ起動 → 画面遷移 → UI要素のidentifierと座標を記録
  2. Generator: テスト計画のidentifierをそのまま使用してテストコードを生成
  3. Healer: 壊れたテストを読み込み → デバイスで実際のUI階層を確認 → 正しいセレクタに自動修正

注意点

  • シミュレータ/エミュレータを事前に起動しておく必要がある
  • iOSではmobile_list_elements_on_screenがエラーになることがある(環境依存)
  • 実機の場合は追加の設定が必要(WebDriverAgentなど)

AIによるモバイルテストコード自動生成に興味がある方は、ぜひ試してみてください!🚀

🔗 参考リンク

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?