Elite IOS Simulator Testing Suite
Streamline your workflow with this skill for interact with mobile simulators for app testing. Built for Claude Code with best practices and real-world patterns.
iOS Simulator Testing Suite
A mobile testing skill for automating iOS app testing using Xcode's Simulator, XCTest, and command-line tools to run UI tests, snapshot tests, and performance benchmarks.
When to Use
Choose iOS Simulator Testing when:
- Running automated UI tests for iOS apps across multiple device configurations
- Setting up CI/CD testing pipelines for iOS applications
- Performing visual regression testing with snapshot comparisons
- Testing accessibility features and VoiceOver compatibility
Consider alternatives when:
- Testing on real devices — use Xcode device testing or device farms
- Cross-platform mobile testing — use Appium or Detox
- Testing web views in iOS — use Playwright's WebKit engine
Quick Start
# List available simulators xcrun simctl list devices # Boot a simulator xcrun simctl boot "iPhone 15 Pro" # Run XCTest suite xcodebuild test -workspace MyApp.xcworkspace \ -scheme MyApp \ -destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=17.0' \ -resultBundlePath TestResults # Take a screenshot of the simulator xcrun simctl io booted screenshot screenshot.png
import XCTest class LoginUITests: XCTestCase { let app = XCUIApplication() override func setUp() { super.setUp() continueAfterFailure = false app.launchArguments = ["--uitesting"] app.launch() } func testLoginWithValidCredentials() { let emailField = app.textFields["email-field"] XCTAssertTrue(emailField.waitForExistence(timeout: 5)) emailField.tap() emailField.typeText("[email protected]") let passwordField = app.secureTextFields["password-field"] passwordField.tap() passwordField.typeText("password123") app.buttons["login-button"].tap() let dashboardTitle = app.staticTexts["Welcome"] XCTAssertTrue(dashboardTitle.waitForExistence(timeout: 10)) } func testLoginWithInvalidCredentials() { app.textFields["email-field"].tap() app.textFields["email-field"].typeText("[email protected]") app.secureTextFields["password-field"].tap() app.secureTextFields["password-field"].typeText("wrongpass") app.buttons["login-button"].tap() let errorAlert = app.alerts["Error"] XCTAssertTrue(errorAlert.waitForExistence(timeout: 5)) errorAlert.buttons["OK"].tap() } func testAccessibility() { let emailField = app.textFields["email-field"] XCTAssertTrue(emailField.isAccessibilityElement) XCTAssertNotNil(emailField.label) // Verify all interactive elements have accessibility labels let buttons = app.buttons.allElementsBoundByIndex for button in buttons { XCTAssertFalse(button.label.isEmpty, "Button missing accessibility label: \(button)") } } }
Core Concepts
Testing Levels
| Level | Framework | Speed | Coverage |
|---|---|---|---|
| Unit Tests | XCTest | Fast | Logic, models, services |
| Integration | XCTest | Medium | API, database, networking |
| UI Tests | XCUITest | Slow | User flows, navigation |
| Snapshot | swift-snapshot-testing | Medium | Visual regression |
| Performance | XCTest measure | Medium | CPU, memory, render time |
| Accessibility | XCTest + Audit | Medium | VoiceOver, Dynamic Type |
CI/CD Pipeline for iOS Testing
#!/bin/bash # ios-ci-test.sh set -euo pipefail SCHEME="MyApp" WORKSPACE="MyApp.xcworkspace" DESTINATION="platform=iOS Simulator,name=iPhone 15 Pro,OS=17.0" RESULT_BUNDLE="TestResults" echo "=== Building for Testing ===" xcodebuild build-for-testing \ -workspace "$WORKSPACE" \ -scheme "$SCHEME" \ -destination "$DESTINATION" \ -derivedDataPath DerivedData \ | xcpretty echo "=== Running Unit Tests ===" xcodebuild test-without-building \ -workspace "$WORKSPACE" \ -scheme "$SCHEME" \ -destination "$DESTINATION" \ -derivedDataPath DerivedData \ -only-testing:"${SCHEME}Tests" \ -resultBundlePath "${RESULT_BUNDLE}/unit" \ | xcpretty --report junit echo "=== Running UI Tests ===" xcodebuild test-without-building \ -workspace "$WORKSPACE" \ -scheme "$SCHEME" \ -destination "$DESTINATION" \ -derivedDataPath DerivedData \ -only-testing:"${SCHEME}UITests" \ -resultBundlePath "${RESULT_BUNDLE}/ui" \ | xcpretty --report junit echo "=== Generating Coverage Report ===" xcrun xccov view --report "${RESULT_BUNDLE}/unit.xcresult" --json > coverage.json
Configuration
| Option | Description | Default |
|---|---|---|
device | Simulator device name | "iPhone 15 Pro" |
os_version | iOS version to simulate | Latest |
scheme | Xcode scheme to test | Project default |
test_plan | Xcode test plan to use | Default |
parallel_testing | Run tests in parallel | true |
retry_count | Retry failed tests | 0 |
screenshot_on_failure | Capture screenshot on test failure | true |
coverage_threshold | Minimum code coverage percentage | 0 |
Best Practices
- Use accessibility identifiers for UI test element queries instead of text content — identifiers are stable across localizations and text changes, making tests resilient to copy updates
- Run tests in parallel across multiple simulator instances to reduce CI pipeline time —
xcodebuild -parallel-testing-enabled YESdistributes tests across available cores - Separate unit and UI tests into different test targets so unit tests run quickly in seconds while slower UI tests run only when needed, and CI can report results independently
- Reset simulator state between test runs with
xcrun simctl eraseto ensure tests do not depend on previous test state or cached data - Use XCTest's
measureblocks for performance regression testing — set baselines for critical operations and fail the build if performance degrades beyond acceptable thresholds
Common Issues
Simulator boot failures in CI: CI machines may not have the required simulator runtime installed. Pre-install specific simulator runtimes in CI setup, use xcrun simctl list runtimes to verify availability, and fall back to alternative device/OS combinations if the preferred one is unavailable.
Flaky UI tests due to animations: UI tests interact with elements that are still animating, causing random failures. Disable animations in test mode with UIView.setAnimationsEnabled(false) in your app's launch arguments handler, and use waitForExistence(timeout:) instead of fixed delays.
Tests passing locally but failing in CI: Timing differences, screen resolution, and keyboard behavior differ between local and CI environments. Use explicit waits for element existence, handle keyboard dismissal explicitly, and set a consistent locale and calendar in the test setup.
Reviews
No reviews yet. Be the first to review this template!
Similar Templates
Full-Stack Code Reviewer
Comprehensive code review skill that checks for security vulnerabilities, performance issues, accessibility, and best practices across frontend and backend code.
Test Suite Generator
Generates comprehensive test suites with unit tests, integration tests, and edge cases. Supports Jest, Vitest, Pytest, and Go testing.
Pro Architecture Workspace
Battle-tested skill for architectural, decision, making, framework. Includes structured workflows, validation checks, and reusable patterns for development.