mcpField
X
xcuitest-skill

xcuitest-skill

github.com/lambdatestTesting & quality384240 installsUpdated 2w ago
Repository

About xcuitest-skill

xcuitest-skill is a guide to XCUITest automation for iOS QA engineers. It covers Swift test patterns, local simulator execution, and TestMu AI cloud workflows.

Key features

  1. 01Swift tests and element queries
  2. 02Tap, input, and gesture actions
  3. 03Assertions and system alert handling
  4. 04Page objects and anti-patterns
  5. 05Local and TestMu AI cloud execution

Use cases

  1. 01Testing an iOS login screen
  2. 02Waiting for and checking UI elements
  3. 03Handling permission alerts
  4. 04Running tests from Xcode or the CLI

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/lambdatest/agent-skills --skill xcuitest-skill

After installing, type/xcuitest-skillin Claude Code to confirm it loaded.

Bundled filesreference/advanced-patterns.mdreference/playbook.md

Details

From the project README

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("[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))
    }
}
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
BadGoodWhy
sleep(5)waitForExistence(timeout:)Unreliable
Element queries without waitAlways waitForExistence firstRace conditions
Hard-coded tap coordinatesAccessibility identifiersScreen sizes vary
Testing in one massive methodSmall focused test methodsBetter 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 "[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
  }'

Quick Reference

TaskCommand
Run from Xcode⌘U or Product → Test
Run from CLIxcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 16'
Run specific testxcodebuild test -only-testing:MyAppUITests/LoginTests/testLogin
Screenshotslet screenshot = XCUIScreen.main.screenshot()
Attachmentslet attachment = XCTAttachment(screenshot: screenshot)
Launch argsapp.launchArguments = ["--uitesting"]
Launch envapp.launchEnvironment = ["ENV": "test"]

Deep Patterns

For advanced patterns, debugging guides, CI/CD integration, and best practices, see reference/playbook.md.

Comments

Markdown supported