
qa-testing-ios
by vasilyu1983
SKILL.md
name: qa-testing-ios description: "iOS testing with XCTest/XCUITest on simulators and devices: layered strategy, determinism/flake control, device matrix selection, CI ergonomics, and repeatable simulator automation."
QA Testing (iOS, Dec 2025) — Quick Reference
This skill enables iOS testing automation via Xcode Simulator and xcodebuild, with a focus on reliable UI tests and layered coverage.
Note: Requires macOS with Xcode installed.
Core references: Apple XCTest docs (https://developer.apple.com/documentation/xctest), simctl (https://developer.apple.com/documentation/xcode/simctl), and Xcode testing guidance (https://developer.apple.com/documentation/xcode/testing-your-apps-in-xcode).
Core QA (Default)
Testing Layers (Use the Smallest Effective Layer)
- Unit tests: business logic, formatting, state machines (fast, deterministic).
- Snapshot tests: view rendering regressions (use sparingly; review diffs).
- Integration tests: persistence, networking adapters, serialization, feature flags.
- UI tests (XCUITest): critical user journeys only; keep thin.
Device Matrix (Simulator vs Real Devices)
- Default: simulators for PR gates; real devices for nightly/release validation.
- Keep the matrix small and risk-based:
- One “small phone”, one “large phone”, and one iPad if the UI supports it [Inference].
- Add OS versions only when you support multiple major releases.
UI Test Flake Control (Determinism)
- Disable/limit animations in test builds where possible.
- Control time: fixed timezone/locale; avoid relying on wall-clock.
- Control network: stub at the boundary for most UI tests; avoid third-party dependencies.
- Control permissions: set predictable permission states; avoid manual prompts.
- Isolation: reset app state between tests; avoid ordering dependence and shared accounts.
CI Economics and Debugging Ergonomics
- PR gate: small smoke UI suite + unit/integration; full UI suite on schedule [Inference].
- Always collect actionable artifacts:
xcresultbundles, screenshots, and logs on failure.
- Prefer “fail fast” diagnostics: assert early on navigation/state instead of letting tests time out.
Do / Avoid
Do:
- Make UI tests independent and idempotent.
- Use test data builders and dedicated test accounts/tenants.
Avoid:
- Relying on test ordering or global state.
- UI tests that require real network access for core flows.
Quick Reference
| Task | Command | When to Use |
|---|---|---|
| List simulators | xcrun simctl list devices | Check available devices |
| Boot simulator | xcrun simctl boot "iPhone 16" | Start simulator |
| Build app | xcodebuild build | Compile iOS app |
| Install app | xcrun simctl install booted app.app | Deploy to simulator |
| Run tests | xcodebuild test | Execute XCTest suite |
| Take screenshot | xcrun simctl io booted screenshot | Capture screen |
| Record video | xcrun simctl io booted recordVideo | Record session |
When to Use This Skill
Claude should invoke this skill when a user requests:
- Build and run iOS app in simulator
- Test iOS app functionality
- Capture screenshots for documentation
- Automate UI testing with XCTest
- Debug iOS app behavior
- Set up iOS CI/CD pipeline
Simulator Management
List Available Simulators
# List all devices
xcrun simctl list devices
# List available runtimes
xcrun simctl list runtimes
# List booted devices only
xcrun simctl list devices | grep "Booted"
Boot and Manage Simulators
# Boot a specific simulator
xcrun simctl boot "iPhone 16 Pro"
# Open Simulator app
open -a Simulator
# Shutdown simulator
xcrun simctl shutdown "iPhone 16 Pro"
# Shutdown all simulators
xcrun simctl shutdown all
# Erase simulator (reset to clean state)
xcrun simctl erase "iPhone 16 Pro"
# Create new simulator
xcrun simctl create "My iPhone" "iPhone 16" "iOS-18-0"
Build and Deploy
Build iOS App
# Build for simulator (Debug)
xcodebuild build \
-project MyApp.xcodeproj \
-scheme MyApp \
-sdk iphonesimulator \
-configuration Debug \
-destination 'platform=iOS Simulator,name=iPhone 16'
# Build with workspace (CocoaPods/SPM)
xcodebuild build \
-workspace MyApp.xcworkspace \
-scheme MyApp \
-sdk iphonesimulator \
-configuration Debug
# Clean build folder
xcodebuild clean \
-project MyApp.xcodeproj \
-scheme MyApp
Install and Launch App
# Install app on booted simulator
xcrun simctl install booted /path/to/MyApp.app
# Launch app
xcrun simctl launch booted com.example.myapp
# Launch and wait for debugger
xcrun simctl launch --wait-for-debugger booted com.example.myapp
# Terminate app
xcrun simctl terminate booted com.example.myapp
# Uninstall app
xcrun simctl uninstall booted com.example.myapp
Testing
Run XCTest Suite
# Run all tests
xcodebuild test \
-project MyApp.xcodeproj \
-scheme MyApp \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 16'
# Run specific test class
xcodebuild test \
-project MyApp.xcodeproj \
-scheme MyApp \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-only-testing:MyAppTests/LoginTests
# Run specific test method
xcodebuild test \
-project MyApp.xcodeproj \
-scheme MyApp \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-only-testing:MyAppTests/LoginTests/testValidLogin
# Run UI tests
xcodebuild test \
-project MyApp.xcodeproj \
-scheme MyAppUITests \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 16'
XCTest Example
// MyAppTests/LoginTests.swift
import XCTest
@testable import MyApp
final class LoginTests: XCTestCase {
var sut: LoginViewModel!
override func setUp() {
super.setUp()
sut = LoginViewModel()
}
override func tearDown() {
sut = nil
super.tearDown()
}
func testValidLogin() async throws {
// Given
let email = "user@example.com"
let password = "password123"
// When
let result = try await sut.login(email: email, password: password)
// Then
XCTAssertTrue(result.isSuccess)
XCTAssertNotNil(sut.currentUser)
}
func testInvalidEmail() {
// Given
let email = "invalid-email"
// When
let error = sut.validateEmail(email)
// Then
XCTAssertEqual(error, .invalidEmail)
}
}
XCUITest Example
// MyAppUITests/LoginUITests.swift
import XCTest
final class LoginUITests: XCTestCase {
var app: XCUIApplication!
override func setUp() {
super.setUp()
continueAfterFailure = false
app = XCUIApplication()
app.launch()
}
func testLoginFlow() {
// Navigate to login
app.buttons["Login"].tap()
// Enter credentials (use accessibilityIdentifier, not labels)
let emailField = app.textFields["emailField"]
emailField.tap()
emailField.typeText("user@example.com")
let passwordField = app.secureTextFields["passwordField"]
passwordField.tap()
passwordField.typeText("password123")
// Submit
app.buttons["submitButton"].tap()
// Verify navigation to dashboard
XCTAssertTrue(app.navigationBars["Dashboard"].waitForExistence(timeout: 5))
}
func testLoginValidation() {
app.buttons["Login"].tap()
app.buttons["submitButton"].tap()
// Verify error message
XCTAssertTrue(app.staticTexts["errorLabel"].waitForExistence(timeout: 2))
}
}
Swift Testing (Xcode 16+)
For new unit and integration tests, prefer Swift Testing over XCTest. See references/swift-testing.md for full guide.
import Testing
@Test func userLoginSucceeds() async throws {
let auth = AuthService()
let result = try await auth.login(email: "user@example.com", password: "pass123")
#expect(result.isSuccess)
#expect(result.user?.email == "user@example.com")
}
// Parameterized test for multiple inputs
@Test(arguments: [
("user@example.com", true),
("invalid", false),
("", false)
])
func emailValidation(email: String, isValid: Bool) {
#expect(EmailValidator.isValid(email) == isValid)
}
When to use which:
- Swift Testing: Unit tests, integration tests (Xcode 16+)
- XCTest/XCUITest: UI tests, performance tests (still required)
Screenshots and Recording
Capture Screenshots
# Take screenshot (PNG)
xcrun simctl io booted screenshot screenshot.png
# Screenshot with specific device
xcrun simctl io "iPhone 16 Pro" screenshot home.png
# Screenshot to clipboard
xcrun simctl io booted screenshot --type=png | pbcopy
Record Video
# Start recording
xcrun simctl io booted recordVideo recording.mov
# Press Ctrl+C to stop recording
# Record with codec
xcrun simctl io booted recordVideo --codec=h264 recording.mp4
Automated Screenshot Script
#!/bin/bash
# capture-screens.sh
DEVICE="iPhone 16 Pro"
OUTPUT_DIR="./screenshots"
APP_BUNDLE="com.example.myapp"
mkdir -p "$OUTPUT_DIR"
# Boot and launch
xcrun simctl boot "$DEVICE"
sleep 5
xcrun simctl install booted ./build/MyApp.app
xcrun simctl launch booted "$APP_BUNDLE"
sleep 3
# Capture screens
xcrun simctl io booted screenshot "$OUTPUT_DIR/01-home.png"
# Navigate and capture
xcrun simctl io booted tap 200 400 # Tap coordinates
sleep 1
xcrun simctl io booted screenshot "$OUTPUT_DIR/02-login.png"
# Cleanup
xcrun simctl shutdown "$DEVICE"
Simulator Interaction
Touch and Input
# Tap at coordinates
xcrun simctl io booted tap 200 400
# Swipe
xcrun simctl io booted swipe 100 500 100 200
# Type text
xcrun simctl io booted type "Hello World"
# Paste from clipboard
xcrun simctl io booted paste
# Press home button
xcrun simctl io booted home
Device Settings
# Set location
xcrun simctl location booted set 37.7749,-122.4194
# Clear location
xcrun simctl location booted clear
# Open URL
xcrun simctl openurl booted "myapp://deep-link"
# Add photo to library
xcrun simctl addmedia booted photo.jpg
# Push notification
xcrun simctl push booted com.example.myapp notification.apns
Notification Payload
// notification.apns
{
"aps": {
"alert": {
"title": "Test Notification",
"body": "This is a test push notification"
},
"badge": 1,
"sound": "default"
}
}
CI/CD Integration
GitHub Actions
# .github/workflows/ios.yml
name: iOS Build and Test
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-test:
runs-on: macos-15
steps:
- uses: actions/checkout@v4
- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_16.0.app
- name: Install dependencies
run: |
brew install xcbeautify
pod install || true
- name: Build
run: |
xcodebuild build \
-scheme MyApp \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 16' \
| xcbeautify
- name: Test
run: |
xcodebuild test \
-scheme MyApp \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-resultBundlePath TestResults \
| xcbeautify
- name: Upload Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: TestResults
Optional: AI / Automation
Do:
- Use AI to expand test ideas from user journeys and failure modes; keep only cases you can automate deterministically.
- Use AI to summarize
xcresultfailures and cluster flakes; verify by reproducing with controlled conditions.
Avoid:
- Generating UI tests that depend on timing/sleeps or real network as the default.
- Accepting AI-proposed selectors/assertions without validating accessibility labels and stable UI structure.
Navigation
Resources
- references/swift-testing.md — Swift Testing framework (Xcode 16+, recommended for new tests)
- references/simulator-commands.md — Complete simctl reference
- references/xctest-patterns.md — XCTest/XCUITest patterns and fixtures
- data/sources.json — Apple documentation links
Templates
- assets/template-ios-ui-test-stability-checklist.md — iOS UI test stability checklist (isolation/determinism/device matrix)
Related Skills
- ../software-mobile/SKILL.md — iOS/Swift development
- ../qa-testing-strategy/SKILL.md — General testing strategies
- ../ops-devops-platform/SKILL.md — CI/CD pipelines
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です