🤖

LLM × MCP で iOS アプリの UI テストを自動化してみた話

に公開

こんにちは。株式会社mikanでiOSエンジニアをしている@uk_oasisです。

これは mikan Advent Calendar 2025 17日目の記事です。

16日目の記事はToshiさんの「なぜ"先生の温度感が高いのに案件が進まない"のか?学校営業を構造で理解する(したい)話」でした。学校営業が一般的なBtoB営業とどう違うのか?ということが、わかりやすくまとまっているので、興味ある方はぜひ読んでみてください!

TL;DR

  • Claude Code + MCP (XcodeBuild / iOS-simulator) を使い、mikanアプリの単語学習をビルド・インストール・操作・録画まで自然言語指示で自動実行。
  • Tests/UITests 配下にREADME・テンプレート・WordStudy.mdを整備し、accessibilityLabel("正解の選択肢") を付けるなどUI側を調整して再現性あ
    るプロンプトを構築。
  • 自然言語で素早く改善サイクルを回せる一方、token消費やCI/CDでの安定運用、画面ごとの追加調整が課題として残るため以下で詳細を整理。

はじめに

mikan Advent Calendar 2025 10日目に@sone「Claude × Maestro MCP で iOS アプリの E2E テストを実装してみた」 を書いてくれていましたが 、iOSチームでテストの機運が高まってきています!

Maestro と MCP を組み合わせて、iOS アプリの操作を自動化するあのアプローチは、 実際のプロダクト開発でもすぐに応用できそうな内容で、読んでいて刺激を受けました。

https://zenn.dev/mikantechnology/articles/5cd6f10fbecd86

そして、あの記事を読みながらふと思い出したのが、「そういえば自分も、以前に 別の方向性で UI テスト自動化を試していた な」ということでした。LLM を使うという点こそ共通しているものの、テストコードを書かずに LLM が xcodebuild や iOS Simulator を直接操作する という、少し違ったアプローチです。

せっかくなので、同僚が紹介してくれた手法と並べて読んでもらえると面白いかも? という気持ちもあって、今回その内容をまとめてみました。

背景:なぜ XCUITest を使わず LLM × MCP にしたのか?

従来方式(XCUITest)には次の課題があります:

  1. UI の変更でテストが壊れやすい
  2. テストコードの作成に工数がかかる
  3. 変更のたびにテストコードのメンテが必要
  4. そもそも “UI を見て判断する” テストは人間的でコード化が難しい箇所が多い

特に4に課題を感じており、導入のコストが高いなと考えていました。 そんな時以下の記事を見て、自然言語の会話で実行できそう!と感じたのが、試してみるきっかけになりました!

https://techblog.spiderplus.co.jp/entry/2025/05/23/160000

記事を読んで、MCPを利用してLLM から XcodeBuild のビルド実行iOS シミュレータ操作 ができることを知り、普段から利用しているClaude Codeで実行してみるということを考えました。

全体アーキテクチャ(図解)

以下は、今回採用したテスト実行フローの図です。

目標

今回の検証では、Claude Code が自然言語の指示だけで「mikanのiOSアプリをiOS シミュレータで起動 → 単語の 4 択学習を進める → 画面録画やスクリーンショットを取得 → Markdown テストレポートを生成」までを一気通貫で実行できるかを確かめることにしました。

セットアップ

環境

macOS Tahoe 26.0.1
Xcode 26.1.1
Claude Code 2.0.65
XcodeBuildMCP Server
iOS-simulator-MCP Server

Claude CodeはiOSチームですでに日常的に使っていたため、リポジトリ直下に CLAUDE.md(LLMへ共有したい文脈・手順をまとめる設定ファイル)が置いてある状態からスタートしました。はじめて触る場合はこのファイルを用意してプロジェクトルートに配置しておくと、LLMに文脈を共有しやすくなります。今回の検証では、次のような方針で CLAUDE.md を更新しました。

  1. 「プロジェクト概要」「利用するMCP」「テスト時の注意点」といった章立てを用意し、LLMに覚えておいてほしいルールを明文化する。
  2. MCPごとに実行コマンド・依存関係・失敗時のリトライ方法を箇条書きでまとめる。
  3. UIテスト関連の情報は Tests/UITests/README.md に集約し、CLAUDE.md から参照リンクを貼る。

たとえば今回の追記は以下のような内容です。

## MCP Servers(UIテスト用)
- ios-simulator: `npx -y @joshuayoes/ios-simulator-mcp`
	- 要件: Xcode 26+, idb-companion, 起動済みの iPhone 17 Pro Simulator
- XcodeBuildMCP: `npx -y @modelcontextprotocol/server-xcodebuild`
    - 要件: `mikan.xcworkspace`, Scheme `mikan_stg`

## UIテスト実行時の注意
- 詳細手順や期待値は `Tests/UITests/README.md` を参照。
- WordStudy.md の TestCase をベースに自然言語プロンプトを渡す。
- 失敗時は `Results/` にレポートとスクショを残す。

必要なMCPを導入

セットアップ節で記載した CLAUDE.md の MCP 定義(ios-simulator と XcodeBuildMCP)をそのまま利用しました。追加作業として、 ~/.claude.jsonに以下を追記しました。

{
  "mcpServers": {
    "ios-simulator": {
      "command": "npx",
      "args": ["-y", "@joshuayoes/ios-simulator-mcp"]
    },
    "XcodeBuildMCP": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-xcodebuild"]
    }
  }
}

https://github.com/joshuayoes/ios-simulator-mcp

https://github.com/cameroncooke/XcodeBuildMCP

また、ios-simulatorを設定する際に、idbを求められたので合わせてインストールしました。

brew tap facebook/fb
brew install idb-companion

実際に手を動かした内容

単語学習のテストを実施

まずは以下の自然言語で書いた手順をそのまま Claude Code に渡し、単語の4択学習を通すフローを実行しました。

### テスト手順

- 教材一覧タブを開く
- TOEIC 500点レベルを探す
- はじめる、もしくは、前回の続きを実行して、単語学習を開始する
- 選択肢を選択する
- 結果画面が表示されたら、左上のバツボタンをタップして学習を終える

これだけで学習がスタートして、結果画面を閉じるまで完了できました!1回目からここまで動くのか・・・LLMの力凄まじいな・・・と感じました。

ただ、見ていると解答ボタンを押せている時、押せていない時があり、ボタンを認識するのに手間取っていました。そこで、Claude Codeが正解のボタンを識別できるように以下のaccessibilityLabelをつけるようにしました。

.accessibilityLabel("正解の選択肢")

その後実行すると学習開始から完了までの動きが確認できました!
シンプルな動きなので、トラブルはないものなのですが、UIテストでここまで実行させるまでの導入が結構大変なので、ここまで簡単なことに驚きました!

テスト用の設定を作成

動くことが確認できたので、運用していけるように以下のような最低限の環境整備を行いました。

肥大化したCLAUDE.mdを整理

CLAUDE.mdのテストに関する項目が大きくなってきた。また、テスト環境について初見のメンバーに対しての情報共有を兼ねて、READMEに分離することにしました。CLAUDE.mdからはこのREADMEを参照するようにして、テストに関する情報はREADMEに集約することにしました。

テスト結果を保存するためのディレクトリを整備

上記のREADMEを置く場所を考えた時、テストを集約した環境が必要と考え、以下のような構成でテストや関係する情報を集約して配置するようにしました。

Tests/
├── UITests
│   ├── README.md (テストに関する情報)
│   ├── Results
│   │   ├── WordStudy_TestReport_20250111.md (テスト結果)
│   │   └── ...
│   ├── UITestTemplate.md (UIテストを作成する時の雛形)
│   └── WordStudy.md (実際のテスト)

テストの追加を容易にするために、テンプレート追加

テストケースを追加するだけで良いようにテンプレートを追加しました。統一された表記にしておくとClaude Codeが把握しやすくなるというのも狙いです。

# UIテスト作成テンプレート

このテンプレートを使用して、mikanアプリの特定機能に対するUIテストの手順を記述してください。

## テンプレート使用方法

1. `[FEATURE_NAME]`を対象機能名に置換
2. テスト手順を具体的に記述
3. Claude Codeにファイルを渡してテスト実行を依頼

---

## UIテストケース: [FEATURE_NAME]

### テスト手順

- [手順1]
- [手順2]
- [手順3]
- [手順4]
- [手順5]

---

**Claude Code実行指示**: 上記の手順に従って、mikanアプリの[FEATURE_NAME]機能のUIテストを実行してください。

実際に作成したUIテストと実行

前処理などの情報はREADMEに移動したため、シンプルな指示だけで良くなりました 🎉

# 単語学習

## UIテストケース: WordStudy

### テスト手順

- 教材一覧タブを開く
- TOEIC 500点レベルを探す
- はじめる、もしくは、前回の続きを実行して、単語学習を開始する
- "正解の選択肢"となっている選択肢を選択する
- 結果画面が表示されたら、左上のバツボタンをタップして学習を終える

---

**Claude Code実行指示**: 上記の手順に従って、mikanアプリのWordStudy機能のUIテストを実行してください。

実行した内容の一部はこんな感じです!(全てを表示すると長くなってしまうので、一部抜粋とさせていただきました 🙏)

試してみてどうだったか?

実際試してみて、良い部分が多かったですが、実運用で利用するためには課題を感じることもあります。

良かったところ

  • 自然言語で書ける
  • UI 状態の “目視判断に近い判定” を Claude Code が代行
  • 実行状況を見ながら、気づいたタイミングで改善できる

課題を感じたところ

  • tokenを多く消費するので上限に達しやすい
  • CI/CDでの安定運用にはまだ工夫が必要
  • 想定する挙動にするためにはアプリ側も一定の修正は必要

特にtokenの消費が激しいところは大きな課題で、シミュレータ操作時に画面の状態を読み込んで、判断しているため、tokenを多く消費してしまいます。画面を都度読み込まなくても良いようにする工夫が必要になりそうです。

もう一つの大きな課題は、実行環境です。今回はお試しだったので自分のMac上で実行しましたが、実際の運用ではCI環境で実行させることになるので、そういった環境でも実行可能にする必要があります。

気になる課題は以下の三つ

  1. シミュレータの安定起動
    • GitHub ActionsなどのCI環境でシミュレータが安定起動しない
    • ヘッドレス環境での画面判定の精度が下がる
  2. 認証情報の管理
    • Claude APIキーなどの機密情報をどう扱うか
  3. 実行時間の長さ
    • LLMの推論時間により、1テストあたり数分かかる

クリアすべき課題はあるのですが、自然言語で書けて、細かく指示を出さなくても解決しながらテストを進めてくれる部分は大きなメリットだなと感じました!

特に良いと感じたのは、改善のサイクルが回しやすいことです。テスト実行の過程を見ていて、時間がかかっているのであれば、時間がかかっていますがどうすれば早くなりますか?などの改善自体もLLMと対話しながらでき、実行から即改善というサイクルが非常に心地よかったです。

さいごに

振り返ってみると、今年は本当にLLMと向き合い続けた一年でした。今回は UI テストの観点から LLM を紹介しましたが、実際にはコードを書くときのアドバイス(特に命名で悩む時間が大幅に減りました)、バグ調査の伴走、レポート作成の補助など、日々の仕事のあらゆる場面で助けられています。そのおかげで、これまで時間を取られていた作業が驚くほど効率化され、生産的なことにより多くの時間を割けるようになったと感じています。

一方で、便利さに頼り切ってしまうと「LLM がないと仕事できない人」になってしまいそうで、そこは正直ちょっと怖さもあります。だからこそ、これからも手を動かす力はしっかり維持しつつ、LLMとうまく付き合っていきたいと思っています。

mikan blog

Discussion