agentsclimarketplace

Ios ui testing

Skill almasumdev/awesome-ios-agent-skills/.github/skills/testing_and_automation/ios-ui-testing

Expert guidance on XCUITest — accessibility identifiers, robust element queries, screenshot flows, test plans, and CI stability. Use when writing end-to-end UI tests.From its SKILL.md

Install
npx -y skills add almasumdev/awesome-ios-agent-skills --skill ios-ui-testing

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

5.4 KB, ~1.2k tokens by cl100k_base, as published. Nobody here has run it

iOS UI Testing with XCUITest

Instructions

UI tests are expensive and flaky by default. Invest in stable selectors, deterministic launch state, and screenshot artifacts so failures are diagnosable.

1. Accessibility Identifiers Over Labels

Never query by localized label. Assign stable identifiers:

Button("Sign in") { signIn() }
    .accessibilityIdentifier("signInButton")

TextField("Email", text: $email)
    .accessibilityIdentifier("emailField")

Query them:

let app = XCUIApplication()
app.textFields["emailField"].typeText("[email protected]")
app.buttons["signInButton"].tap()

2. Launch Arguments & Environment

Drive app state from the test rather than tapping through it:

func test_empty_inbox() {
    let app = XCUIApplication()
    app.launchArguments = ["-UITests", "-InboxState", "empty"]
    app.launchEnvironment = ["API_BASE_URL": "http://localhost:8080"]
    app.launch()

    XCTAssertTrue(app.staticTexts["inbox.empty.title"].waitForExistence(timeout: 2))
}

Inside the app:

#if DEBUG
if CommandLine.arguments.contains("-UITests") {
    Bootstrap.useInMemoryStore()
    if let idx = CommandLine.arguments.firstIndex(of: "-InboxState"),
       CommandLine.arguments.indices.contains(idx + 1) {
        InboxSeed.apply(CommandLine.arguments[idx + 1])
    }
}
#endif

3. Waiting

Never Thread.sleep. Use waitForExistence(timeout:) or XCTWaiter:

let row = app.cells["article.row.123"]
XCTAssertTrue(row.waitForExistence(timeout: 5))
row.tap()

For more complex waits:

let predicate = NSPredicate(format: "isHittable == true")
let exp = expectation(for: predicate, evaluatedWith: button)
wait(for: [exp], timeout: 5)

4. Page Objects

Encapsulate queries in page objects so tests read like user flows:

struct LoginScreen {
    let app: XCUIApplication
    var email: XCUIElement { app.textFields["emailField"] }
    var password: XCUIElement { app.secureTextFields["passwordField"] }
    var signIn: XCUIElement { app.buttons["signInButton"] }

    func signIn(as email: String, password pw: String) {
        self.email.tap(); self.email.typeText(email)
        self.password.tap(); self.password.typeText(pw)
        self.signIn.tap()
    }
}

5. Screenshots and Attachments

Capture a screenshot on failure:

override func tearDown() {
    if let failureCount = testRun?.failureCount, failureCount > 0 {
        let shot = XCUIScreen.main.screenshot()
        let attach = XCTAttachment(screenshot: shot)
        attach.name = name
        attach.lifetime = .keepAlways
        add(attach)
    }
    super.tearDown()
}

For release artifacts (App Store screenshots) use fastlane snapshot or a custom routine that walks every target device + locale.

6. Test Plans

Use Xcode Test Plans (.xctestplan) to:

  • Run subsets (smoke vs full) from CI.
  • Set default launch arguments.
  • Repeat flaky tests -retry-tests-on-failure.
  • Randomize order to catch hidden coupling.

7. Hermetic Tests

  • Stub the network with a local mock server or URLProtocol injection gated by a launch argument.
  • Reset UserDefaults and the Keychain for the app's suite at test start.
  • Seed SwiftData/Core Data with an in-memory store during UI tests.
if ProcessInfo.processInfo.arguments.contains("-UITests") {
    UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)
}

8. Flakiness Patrol

Common causes and fixes:

SymptomFix
Element not found intermittentlyQuery by identifier, use waitForExistence
Scroll-to-cell missesapp.cells.element(matching: ...).firstMatch + scroll
Animations interfereUIView.setAnimationsEnabled(false) in test hook
Keyboard covers the fieldUse hardware keyboard flag in simulator settings
Timeouts on CI onlyLaunch simulator cold, pre-boot in CI step

9. Running on CI

  • Use xcodebuild test-without-building for speed.
  • Enable Parallel Distributed Testing only when tests are hermetic.
  • Collect .xcresult bundles and upload as artifacts; screenshots travel inside them.
xcodebuild -workspace MyApp.xcworkspace \
           -scheme MyAppUITests \
           -testPlan Smoke \
           -destination 'platform=iOS Simulator,name=iPhone 15' \
           -resultBundlePath build/UITests.xcresult \
           test-without-building

10. Accessibility-Driven Testing

UI tests double as accessibility audits. If you can't query an element, neither can VoiceOver. Fix identifiers and traits — see the ios-accessibility skill.

Checklist

  • All interactive elements have stable accessibilityIdentifiers.
  • Tests launch with flags to drive initial state; no tap-through seed data.
  • No Thread.sleep; waits use waitForExistence or predicates.
  • Pages are modeled as page objects.
  • Failure screenshots and .xcresult bundles upload from CI.
  • Flaky tests are retried deterministically via a test plan, not hidden.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,764. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.