Skip to content
Mobile
Skill

/swift-testing

Writes and migrates Swift Testing framework tests with @Test, @Suite, #expect, #require, confirmation, traits, withKnownIssue, Attachment.record, processExitsWith exit tests and capture lists, Test.cancel, Issue.record warnings/manual failures, XCTest-to-Swift Testing migration,

From plugin
swift-ios-skills
1.3k86 skills1 MCP

Claude Code

$ npx -y skills add dpearson2699/swift-ios-skills --skill swift-testing --agent claude-code

SKILL.md

swift-testing.SKILL.md
name: swift-testing
description: "Writes and migrates Swift Testing framework tests with @Test, @Suite, #expect, #require, confirmation, traits, withKnownIssue, Attachment.record, processExitsWith exit tests and capture lists, Test.cancel, Issue.record warnings/manual failures, XCTest-to-Swift Testing migration, Xcode 27 interoperability modes, XCUITest UI-test boundaries, performance/snapshot boundaries, mocking, async patterns, and test organization. Use when writing tests, converting XCTest assertions such as XCTUnwrap or XCTFail, reviewing advanced Swift Testing API availability, or deciding when to keep XCTest/XCUITest."

Swift Testing

Swift Testing is the modern testing framework for Swift (Xcode 16+, Swift 6+). Prefer it for new unit tests. Keep XCTest where migration is still in progress, and use XCTest for UI automation, performance APIs, Objective-C exception tests, and common snapshot-test tooling.

Contents

  • [Basic Tests](#basic-tests)
  • [`@Test Traits`](#test-traits)
  • [#expect and #require](#expect-and-require)
  • [`@Suite and Test Organization`](#suite-and-test-organization)
  • [Execution Model](#execution-model)
  • [XCTest Migration Boundaries](#xctest-migration-boundaries)
  • [Known Issues](#known-issues)
  • [Additional Patterns](#additional-patterns)
  • [Common Mistakes](#common-mistakes)
  • [Test Attachments](#test-attachments)
  • [Exit Testing](#exit-testing)
  • [Version-Gated APIs](#version-gated-apis)
  • [Review Checklist](#review-checklist)
  • [References](#references)

---

Basic Tests

import Testing

@Test("User can update their display name")
func updateDisplayName() {
    var user = User(name: "Alice")
    user.name = "Bob"
    #expect(user.name == "Bob")
}

`@Test` Traits

@Test("Validates email format")                                    // display name
@Test(.tags(.validation, .email))                                  // tags
@Test(.disabled("Server migration in progress"))                   // disabled
@Test(.enabled(if: ProcessInfo.processInfo.environment["CI"] != nil)) // conditional
@Test(.bug("https://github.com/org/repo/issues/42"))               // bug reference
@Test(.timeLimit(.minutes(1)))                                     // time limit
@Test("Timeout handling", .tags(.networking), .timeLimit(.seconds(30))) // combined

#expect and #require

// #expect records failure but continues execution
#expect(result == 42)
#expect(name.isEmpty == false)
#expect(items.count > 0, "Items should not be empty")

// #expect with error type checking
#expect(throws: ValidationError.self) {
    try validate(email: "not-an-email")
}

// #expect with specific error value
#expect {
    try validate(email: "")
} throws: { error in
    guard let err = error as? ValidationError else { return false }
    return err == .empty
}

// #require records failure AND stops test (like XCTUnwrap)
let user = try #require(await fetchUser(id: 1))
#expect(user.name == "Alice")

// #require for optionals -- unwraps or fails
let first = try #require(items.first)
#expect(first.isValid)

**Rule: Use `#require` when subsequent assertions depend on the value. Use `#expect` for independent checks.**

`@Suite` and Test Organization

See [references/testing-patterns.md](references/testing-patterns.md) for suite organization, confirmation patterns, known-issue handling, and execution-model details.

Execution Model

Swift Testing runs tests in parallel by default. Do not assume test order, shared suite instances, or exclusive access to mutable state unless you explicitly design for it.

@Suite(.serialized)
struct KeychainTests {
    @Test func storesToken() throws { /* ... */ }
    @Test func deletesToken() throws { /* ... */ }
}

Use `.serialized` when a test or suite must run one-at-a-time because it touches shared external state. It does not make unrelated tests outside that scope run serially.

**Rules:**

  • Each test must set up its own state.
  • Shared mutable globals are a bug unless protected or intentionally serialized.
  • `@Suite(.serialized)` is for exclusive execution, not for expressing logical ordering between tests.
  • If tests depend on sequence, combine them into one test or move the sequence into shared helper code.

XCTest Migration Boundaries

Swift Testing unit tests do not inherit from `XCTestCase`. Declare `@Test` on free functions or methods on suite types such as `struct`, `class`, or `actor`; use `static` or `class` methods when instance fixtures are unnecessary.

XCTest and Swift Testing can coexist during migration. Migrate one file or suite at a time, compare discovery/pass/fail/skip counts, and keep UI automation, performance benchmarks, and common snapshot flows on XCTest/XCUITest or snapshot tooling. Separate files or targets when that makes runner expectations clearer.

For Xcode 27-era mixed helpers, check the configured interoperability mode rather than claiming cross-framework APIs are forbidden. Older test plans inherit `limited`; new projects use `complete`; `strict` and `none` are also available. Prefer `complete` or `strict` during migration and use `SWIFT_TESTING_XCTEST_INTEROP_MODE` for SwiftPM when needed. See [references/testing-advanced.md](references/testing-advanced.md) for the mode matrix and toolchain gates.

Do not mechanically replace every XCTest assertion with `#expect`; preserve required unwraps and unconditional failures with these migration defaults:

  • `XCTAssert*` -> `#expect(...)`
  • `XCTUnwrap` or any value required by later checks -> `try #require(...)`
  • `XCTFail("...")` or manual unconditional issues -> `Issue.record("...")`
  • UI tests, performance benchmarks, and common snapshot-test flows stay on XCTest/XCUITest or snapshot tooling.
  • Put `@available` on individual `@Test` functions, not on suite types or their containing types.

See [references/testing-patterns.md](references/testing-patterns.md) for migration examples and [references/testing-advanced.md](refere

Read more
@dpearson2699
Ships withswift-ios-skills

86 agent skills optimized for iOS 26+ development with Swift 6.3 and modern Apple frameworks.

1.3k86 skills1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
1.3k
Star history of dpearson2699/swift-ios-skills on GitHub, a chart by star-history.com

Other skills on swift-ios-skills.

alarmkit

alarmkit

Implement AlarmKit alarms and countdown timers for iOS and iPadOS with Lock Screen, Dynamic Island, StandBy, and paired Apple…

app-clips

app-clips

Build iOS App Clips with invocation URLs, App Clip Codes, NFC, QR codes, Safari banners, Maps, Messages, target setup, App Store…

More skills from other plugins.

deep-code-review

deep-code-review

In-depth design-focused code review - understands codebase context before evaluating PR changes, posts structured feedback to…

expo1d53k
@expo@expoInstall Skill
modlens

modlens

Plug-in vision for text-only models. Hard rule: when a file path or URL with an image extension (.png, .jpg, .jpeg, .webp, .gif,…

modlens2w4.6k
@liustack@liustackInstall Skill
appllama-app-design-skill

appllama-app-design-sk…

Build native-feeling, benchmark-quality mobile app screens (Expo / React Native). Use when designing or implementing any mobile…

appllama2mo3.9k
@appllama@appllamaInstall Skill

Questions about swift-testing.

Which coding agents can use swift-testing?
swift-testing installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.