xctest-ios
Skill2 starsChanged 3 months agoMITView on GitHub
Native iOS testing with XCTest: unit tests, UI automation, performance, snapshot testing, parallel execution, and CI/CD with Xcode Cloud.
- Installs packages
Files
XCTest iOS
Invoke when user asks about XCTest, iOS testing, Swift unit tests, or native iOS automation.
Core Principles
- First-party: Apple's framework, no third-party dependencies.
- Unified: Unit, UI, and performance tests in one framework.
- Tooling: Xcode integration, parallel testing, code coverage, CI with Xcode Cloud.
Project Setup
// In Xcode: File > New > Target > UI Testing Bundle
// Or add test target to Package.swift
// Package.swift
targets: [
.target(name: "MyApp"),
.testTarget(
name: "MyAppTests",
dependencies: ["MyApp"]
),
]Unit Tests (XCTestCase)
import XCTest
@testable import MyApp
final class CalculatorTests: XCTestCase {
var calculator: Calculator!
override func setUp() {
super.setUp()
calculator = Calculator()
}
override func tearDown() {
calculator = nil
super.tearDown()
}
func testAddition() {
XCTAssertEqual(calculator.add(2, 3), 5)
}
func testDivisionByZeroThrows() {
XCTAssertThrowsError(try calculator.divide(10, 0)) { error in
XCTAssertEqual(error as? CalculatorError, .divisionByZero)
}
}
// Async test
func testFetchUser() async throws {
let user = try await api.fetchUser(id: 1)
XCTAssertEqual(user.name, "Alice")
}
// Parameterized with enum
func testSquares() {
let cases = [(input: 2, expected: 4), (input: 3, expected: 9)]
for c in cases {
XCTAssertEqual(calculator.square(c.input), c.expected)
}
}
}UI Tests (XCUIApplication)
import XCTest
final class LoginUITests: XCTestCase {
let app = XCUIApplication()
override func setUp() {
continueAfterFailure = false
app.launch()
}
func testSuccessfulLogin() {
let emailField = app.textFields["emailTextField"]
let passwordField = app.secureTextFields["passwordTextField"]
let loginButton = app.buttons["loginButton"]
let welcomeLabel = app.staticTexts["welcomeLabel"]
emailField.tap()
emailField.typeText("alice@test.com")
passwordField.tap()
passwordField.typeText("password123")
loginButton.tap()
XCTAssertTrue(welcomeLabel.waitForExistence(timeout: 5))
XCTAssertEqual(welcomeLabel.label, "Welcome, Alice")
}
func testInvalidPasswordShowsError() {
app.textFields["emailTextField"].tap()
app.textFields["emailTextField"].typeText("alice@test.com")
app.secureTextFields["passwordTextField"].tap()
app.secureTextFields["passwordTextField"].typeText("wrong")
app.buttons["loginButton"].tap()
let errorAlert = app.alerts["Error"]
XCTAssertTrue(errorAlert.waitForExistence(timeout: 3))
}
}Performance Tests
func testTableScrollPerformance() throws {
measure(
metrics: [
XCTClockMetric(),
XCTCPUMetric(),
XCTMemoryMetric(),
]
) {
let table = app.tables.element(boundBy: 0)
table.swipeUp(velocity: .fast)
table.swipeDown(velocity: .fast)
}
}Snapshot Testing (Point-Free)
// Package.swift: .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.15.0")
import SnapshotTesting
import XCTest
func testLoginScreen() {
let view = LoginView()
let controller = UIHostingController(rootView: view)
assertSnapshot(
of: controller,
as: .image(on: .iPhone13Pro)
)
}Parallel Test Execution
# xcodebuild with parallel testing
xcodebuild test \
-project MyApp.xcodeproj \
-scheme MyApp \
-destination 'platform=iOS Simulator,name=iPhone 15' \
-parallel-testing-enabled YES \
-maximum-parallel-testing-workers 4CI/CD (Xcode Cloud)
# ci_post_clone.sh
#!/bin/bash
brew install swiftlint
swiftlint lint --reporter json > swiftlint.json# ci_post_xcodebuild.sh
#!/bin/bash
# Upload coverage to external service
bash <(curl -s https://codecov.io/bash) -J 'MyApp'Accessibility Testing
func testAccessibilityLabels() {
let emailField = app.textFields["emailTextField"]
XCTAssertEqual(
emailField.label,
"Email Address"
)
}Anti-Patterns
| Anti-Pattern | Fix |
sleep(3) in UI tests | Use waitForExistence(timeout:) |
| Accessing views by index | Use accessibilityIdentifier |
| Testing only on iPhone | Add iPad targets |
| No tearDown | Clean state between tests |
| Hardcoded device names in CI | Use xcrun simctl list dynamically |
Quick Reference
| API | Purpose |
XCUIApplication().launch() | Launch app |
.tap() | Tap element |
.typeText("text") | Type text |
.swipeUp() / .swipeDown() | Scroll |
.pinch(withScale: 2) | Pinch zoom |
.twoFingerTap() | Two-finger tap |
.waitForExistence(timeout:) | Wait for element |
XCTAssertTrue / XCTAssertEqual | Assertions |
In this repository45 other files it gives its agents.
- AGENTS.mdAGENTS.md
- allure-reportsSkill
- android-nativeSkill
- appian-lowcodeSkill
- appium-mobileSkill
- behave-bddSkill
- BiDiWave Cross-BrowserSkill
- BiDiWave Network InterceptionSkill
- CDPWave DebuggingSkill
- CDPWave TestingSkill
- db2-oracleSkill
- ebook-writerSkill
- flask-apiSkill
- flet-desktopSkill
- google-crawling-indexingSkill
- google-ranking-appearanceSkill
- google-seo-fundamentalsSkill
- google-seo-monitoringSkill
- jinja2-templatesSkill
- jmeter-load-testingSkill
- k6-load-testingSkill
- karate-api-testingSkill
- octane-almSkill
- paramiko-sshSkill
- postman-api-automationSkill
- prestashop-moduleSkill
- prompt-engineering-best-practicesSkill
- pytest-advancedSkill
- robot-frameworkSkill
- salesforce-devSkill
- sonarqube-quality-gatesSkill
- sql-serverSkill
- Skill NameSkill
- testcontainersSkill
- wave-ecosystem-guideSkill
- WaveXis AccessibilitySkill
- WaveXis CI/CD IntegrationSkill
- wavexis-cli-automationSkill
- wavexis-mcp-agent-integrationSkill
- WaveXis Network TestingSkill
- WaveXis Performance AuditSkill
- WaveXis Session RecordingSkill
- WaveXis Web ScrapingSkill
- webdriverioSkill
- wordpress-themesSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

