xcuitest-skill について
xcuitest-skillは、iOS QAエンジニア向けのXCUITest自動化ガイドです。Swiftのテストパターンに加え、ローカルシミュレーターとTestMu AIクラウドでの実行方法を扱います。
主な機能
- 01Swiftテストと要素クエリ
- 02タップ、入力、ジェスチャー操作
- 03アサーションとシステムアラート処理
- 04ページオブジェクトとアンチパターン
- 05ローカルおよびTestMu AIクラウドでの実行
活用シーン
- 01iOSログイン画面のテスト
- 02UI要素の待機と検証
- 03権限アラートの処理
- 04Xcodeまたはコマンドラインからのテスト実行
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ lambdatest/ agent-skills --skill xcuitest-skillインストール後、Claude Code で/xcuitest-skillを入力して読み込まれたか確認します。
同梱ファイルreference/advanced-patterns.mdreference/playbook.md
詳細
XCUITest 自動化スキル
あなたは XCUITest を専門とするシニア iOS QA エンジニアです。
ステップ 1 — 実行ターゲット
├─ Mentions "cloud", "TestMu", "LambdaTest", "device farm"?
│ └─ TestMu AI cloud (upload IPA + test runner)
│
├─ Mentions "simulator", "local", "Xcode"?
│ └─ Local: Xcode Test Navigator or xcodebuild
│
└─ Default → Local simulator
基本パターン — Swift
基本的なテスト
import XCTest
class LoginTests: XCTestCase {
let app = XCUIApplication()
override func setUpWithError() throws {
continueAfterFailure = false
app.launch()
}
func testLoginWithValidCredentials() {
let emailField = app.textFields["emailInput"]
XCTAssertTrue(emailField.waitForExistence(timeout: 5))
emailField.tap()
emailField.typeText("[email protected]")
let passwordField = app.secureTextFields["passwordInput"]
passwordField.tap()
passwordField.typeText("password123")
app.buttons["loginButton"].tap()
let dashboard = app.staticTexts["Welcome"]
XCTAssertTrue(dashboard.waitForExistence(timeout: 10))
}
func testLoginWithInvalidCredentials() {
app.textFields["emailInput"].tap()
app.textFields["emailInput"].typeText("[email protected]")
app.secureTextFields["passwordInput"].tap()
app.secureTextFields["passwordInput"].typeText("wrong")
app.buttons["loginButton"].tap()
let error = app.staticTexts["Invalid credentials"]
XCTAssertTrue(error.waitForExistence(timeout: 5))
}
}
要素の検索
// By accessibility identifier (best)
app.buttons["loginButton"]
app.textFields["emailInput"]
// By label text
app.staticTexts["Welcome back"]
app.buttons["Submit"]
// By predicate
app.buttons.matching(NSPredicate(format: "label CONTAINS 'Login'")).firstMatch
// By index
app.cells.element(boundBy: 0)
// Existence check
let element = app.buttons["submit"]
XCTAssertTrue(element.waitForExistence(timeout: 10))
操作
element.tap() // Tap
element.doubleTap() // Double tap
element.press(forDuration: 2) // Long press
element.typeText("hello") // Type (field must be focused)
element.swipeUp() // Swipe
element.swipeDown()
element.swipeLeft()
element.swipeRight()
element.pinch(withScale: 2, velocity: 1) // Zoom in
element.rotate(CGFloat.pi, withVelocity: 1) // Rotate
アサーション
XCTAssertTrue(element.exists)
XCTAssertTrue(element.isHittable)
XCTAssertTrue(element.isEnabled)
XCTAssertEqual(element.label, "Expected Label")
XCTAssertEqual(element.value as? String, "Expected Value")
XCTAssertTrue(element.waitForExistence(timeout: 10))
システムアラートの処理
// Auto-handle permission dialogs
addUIInterruptionMonitor(withDescription: "Permission Alert") { alert in
if alert.buttons["Allow"].exists {
alert.buttons["Allow"].tap()
return true
}
return false
}
app.tap() // Trigger the monitor
ページオブジェクトパターン
protocol Page {
var app: XCUIApplication { get }
}
class LoginPage: Page {
let app: XCUIApplication
init(app: XCUIApplication) { self.app = app }
var emailField: XCUIElement { app.textFields["emailInput"] }
var passwordField: XCUIElement { app.secureTextFields["passwordInput"] }
var loginButton: XCUIElement { app.buttons["loginButton"] }
var errorLabel: XCUIElement { app.staticTexts["errorMessage"] }
func login(email: String, password: String) -> DashboardPage {
emailField.tap()
emailField.typeText(email)
passwordField.tap()
passwordField.typeText(password)
loginButton.tap()
return DashboardPage(app: app)
}
}
アンチパターン
| 悪い例 | 良い例 | 理由 |
|---|---|---|
sleep(5) | waitForExistence(timeout:) | 信頼性に欠けます |
| 待機せずに要素を検索する | まず必ず waitForExistence を実行する | 競合状態を防げます |
| タップ座標をハードコードする | アクセシビリティ識別子を使う | 画面サイズは異なるためです |
| 1 つの巨大なメソッドでテストする | 小さく焦点を絞ったテストメソッドを使う | 分離性が向上します |
TestMu AI Cloud
# 1. Create .ipa from Xcode: Product → Archive → Distribute → Ad Hoc
# 2. Upload app and test runner
curl -u "$LT_USERNAME:$LT_ACCESS_KEY" \
-X POST "https://manual-api.lambdatest.com/app/upload/realDevice" \
-F "[email protected]" -F "type=ios"
curl -u "$LT_USERNAME:$LT_ACCESS_KEY" \
-X POST "https://manual-api.lambdatest.com/app/upload/realDevice" \
-F "[email protected]" -F "type=ios"
# 3. Execute on real devices
curl -u "$LT_USERNAME:$LT_ACCESS_KEY" \
-X POST "https://mobile-api.lambdatest.com/framework/v1/xcui/build" \
-H "Content-Type: application/json" \
-d '{
"app": "lt://APP123",
"testSuite": "lt://TEST456",
"device": ["iPhone 16-18", "iPhone 15 Pro-17"],
"build": "XCUITest Cloud Build",
"video": true, "deviceLog": true
}'
クイックリファレンス
| タスク | コマンド |
|---|---|
| Xcode から実行 | ⌘U または Product → Test |
| CLI から実行 | xcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 16' |
| 特定のテストを実行 | xcodebuild test -only-testing:MyAppUITests/LoginTests/testLogin |
| スクリーンショット | let screenshot = XCUIScreen.main.screenshot() |
| 添付ファイル | let attachment = XCTAttachment(screenshot: screenshot) |
| 起動引数 | app.launchArguments = ["--uitesting"] |
| 起動環境変数 | app.launchEnvironment = ["ENV": "test"] |
高度なパターン
高度なパターン、デバッグガイド、CI/CD との統合、ベストプラクティスについては、
reference/playbook.md を参照してください。
コメント
Markdown 対応