Agent-skills xcuitest-skill
install
source · Clone the upstream repo
git clone https://github.com/LambdaTest/agent-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/LambdaTest/agent-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/xcuitest-skill" ~/.claude/skills/lambdatest-agent-skills-xcuitest-skill && rm -rf "$T"
manifest:
xcuitest-skill/SKILL.mdsource content
XCUITest Automation Skill
You are a senior iOS QA engineer specializing in XCUITest.
Step 1 — Execution Target
├─ 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
Core Patterns — Swift
Basic Test
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("user@test.com") 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("wrong@test.com") app.secureTextFields["passwordInput"].tap() app.secureTextFields["passwordInput"].typeText("wrong") app.buttons["loginButton"].tap() let error = app.staticTexts["Invalid credentials"] XCTAssertTrue(error.waitForExistence(timeout: 5)) } }
Element Queries
// 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))
Actions
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
Assertions
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))
Handling System Alerts
// 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
Page Object Pattern
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) } }
Anti-Patterns
| Bad | Good | Why |
|---|---|---|
| | Unreliable |
| Element queries without wait | Always first | Race conditions |
| Hard-coded tap coordinates | Accessibility identifiers | Screen sizes vary |
| Testing in one massive method | Small focused test methods | Better isolation |
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 "appFile=@MyApp.ipa" -F "type=ios" curl -u "$LT_USERNAME:$LT_ACCESS_KEY" \ -X POST "https://manual-api.lambdatest.com/app/upload/realDevice" \ -F "appFile=@MyAppUITests-Runner.ipa" -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 }'
Quick Reference
| Task | Command |
|---|---|
| Run from Xcode | ⌘U or Product → Test |
| Run from CLI | |
| Run specific test | |
| Screenshots | |
| Attachments | |
| Launch args | |
| Launch env | |
Deep Patterns
For advanced patterns, debugging guides, CI/CD integration, and best practices, see
reference/playbook.md.