Use when writing tests with Swift Testing (@Test,
Provides Swift Testing framework support with modern macros (#expect, #require) instead of XCTest. Use when writing Swift tests with @Test, @Suite, or migrating from XCTest assertions to Swift Testing's concise, async-friendly syntax.
/plugin marketplace add johnrogers/claude-swift-engineering/plugin install swift-engineering@claude-swift-engineeringThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Modern testing with Swift Testing framework. No XCTest.
Swift Testing replaces XCTest with a modern macro-based approach that's more concise, has better async support, and runs tests in parallel by default. The core principle: if you learned XCTest, unlearn it—Swift Testing works differently.
| Macro | Use Case |
|---|---|
#expect(expression) | Soft check — continues on failure. Use for most assertions. |
#require(expression) | Hard check — stops test on failure. Use for preconditions only. |
let user = try #require(await fetchUser(id: "123"))
#expect(user.id == "123")
import Testing
@testable import YourModule
@Suite
struct FeatureTests {
let sut: FeatureType
init() throws {
sut = FeatureType()
}
@Test("Description of behavior")
func testBehavior() {
#expect(sut.someProperty == expected)
}
}
| XCTest | Swift Testing |
|---|---|
XCTAssert(expr) | #expect(expr) |
XCTAssertEqual(a, b) | #expect(a == b) |
XCTAssertNil(a) | #expect(a == nil) |
XCTAssertNotNil(a) | #expect(a != nil) |
try XCTUnwrap(a) | try #require(a) |
XCTAssertThrowsError | #expect(throws: ErrorType.self) { } |
XCTAssertNoThrow | #expect(throws: Never.self) { } |
#expect(throws: (any Error).self) { try riskyOperation() }
#expect(throws: NetworkError.self) { try fetch() }
#expect(throws: NetworkError.timeout) { try fetch() }
#expect(throws: Never.self) { try safeOperation() }
@Test("Validates inputs", arguments: zip(
["a", "b", "c"],
[1, 2, 3]
))
func testInputs(input: String, expected: Int) {
#expect(process(input) == expected)
}
Warning: Multiple collections WITHOUT zip creates Cartesian product.
@Test func testAsync() async throws {
let result = try await fetchData()
#expect(!result.isEmpty)
}
@Test func testCallback() async {
await confirmation("callback received") { confirm in
let sut = SomeType { confirm() }
sut.triggerCallback()
}
}
extension Tag {
@Tag static var fast: Self
@Tag static var networking: Self
}
@Test(.tags(.fast, .networking))
func testNetworkCall() { }
#require — Use #expect for most checkszip for paired inputs.serialized — Apply for thread-unsafe legacy testsOverusing #require — #require is for preconditions only. Using it for normal assertions means the test stops at first failure instead of reporting all failures. Use #expect for assertions, #require only when subsequent assertions depend on the value.
Cartesian product bugs — @Test(arguments: [a, b], [c, d]) creates 4 combinations, not 2. Always use zip to pair arguments correctly: arguments: zip([a, b], [c, d]).
Forgetting state isolation — Swift Testing creates a new test instance per test method. BUT shared state between tests (static variables, singletons) still leak. Use dependency injection or clean up singletons between tests.
Parallel test conflicts — Swift Testing runs tests in parallel by default. Tests touching shared files, databases, or singletons will interfere. Use .serialized or isolation strategies.
Not using async naturally — Wrapping async operations in Task { } defeats the purpose. Use async/await directly in test function signature: @Test func testAsync() async throws { }.
Confirmation misuse — confirmation is for verifying callbacks were called. Using it for assertions is wrong. Use #expect for assertions, confirmation for callback counts.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.