.claude/skills/workflow/xctest-patterns/SKILL.md
XCTest patterns for macOS Swift apps. Unit tests, async tests, Core Data tests, mock patterns, and assertion reference. Use when writing or reviewing tests.
npx skillsauth add brdohman/agile-maestro xctest-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
import XCTest
@testable import AppName
final class SomeViewModelTests: XCTestCase {
var sut: SomeViewModel!
var mockService: MockService!
override func setUp() {
super.setUp()
mockService = MockService()
sut = SomeViewModel(service: mockService)
}
override func tearDown() {
sut = nil
mockService = nil
super.tearDown()
}
func testLoadSuccess() async {
// Given
mockService.result = .success(testData)
// When
await sut.load()
// Then
guard case .loaded(let items) = sut.state else { return XCTFail("Expected loaded") }
XCTAssertEqual(items.count, 2)
}
}
@MainActor
func testAsyncStateUpdate() async {
// Given
XCTAssertFalse(sut.isLoading)
// When
await sut.loadData()
// Then
XCTAssertFalse(sut.isLoading)
XCTAssertNotNil(sut.data)
}
final class CoreDataTests: XCTestCase {
var container: NSPersistentContainer!
var context: NSManagedObjectContext!
override func setUp() {
super.setUp()
container = NSPersistentContainer(name: "AppName")
let desc = NSPersistentStoreDescription()
desc.type = NSInMemoryStoreType
container.persistentStoreDescriptions = [desc]
container.loadPersistentStores { _, error in XCTAssertNil(error) }
context = container.viewContext
}
override func tearDown() {
context = nil
container = nil
super.tearDown()
}
}
protocol ServiceProtocol: Sendable {
func fetch() async throws -> [Item]
}
final class MockService: ServiceProtocol, @unchecked Sendable {
var result: Result<[Item], Error> = .success([])
func fetch() async throws -> [Item] { try result.get() }
}
All test data must use TestFixtures helpers. Never hardcode passwords, tokens, or secrets — SAST scanners flag these as real findings.
// In setUp or test methods:
sut.password = TestFixtures.validPassword()
sut.email = TestFixtures.validEmail()
mockService.result = .success(TestFixtures.item())
Check for existing TestFixtures.swift in the test target before creating new helpers.
| Assertion | Use For |
|-----------|---------|
| XCTAssertEqual(a, b) | Value equality |
| XCTAssertTrue/False | Boolean checks |
| XCTAssertNil/NotNil | Optional checks |
| XCTAssertThrowsError | Error throwing |
| XCTFail("message") | Explicit failure |
@MainActor@unchecked Sendable on mocks onlyWhen Xcode MCP is available, prefer these over xcodebuild test:
# Discover available test targets and classes
mcp__xcode__GetTestList(tabIdentifier: "...")
# Run all tests (structured JSON results)
mcp__xcode__RunAllTests(tabIdentifier: "...")
# Run specific tests for story-level QA
mcp__xcode__RunSomeTests(tabIdentifier: "...", tests: ["AppNameTests/ItemListViewModelTests"])
Returns structured results (test name, pass/fail, duration) vs parsing xcodebuild stdout.
See .claude/skills/tooling/xcode-mcp/SKILL.md for full reference.
tools
How to transition workflow state between review stages. Rules for setting review_stage and review_result fields on Stories and Epics.
documentation
Comment structure and rules for task workflow updates. Use when adding any comment to a task during implementation, review, or fix cycles.
testing
Validate task/story/epic/bug/techdebt metadata against schema v2.0. Run after TaskCreate or TaskUpdate to verify compliance. Returns pass/fail with actionable details.
data-ai
Canonical v2.0 metadata schema field reference for epics, stories, and tasks. Use when creating or validating task metadata.