# Swift Testing

> When to activate: Swift Testing framework, XCTest, unit tests, async tests, test macros, parameterized tests, Swift mocking

- Skill: `mattakushi432/swift-testing` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mattakushi432/swift-testing`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mattakushi432/swift-testing/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Mattakushi432 (https://skillmd.com/u/mattakushi432)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mattakushi432/swift-testing

---


# Swift Testing

## Swift Testing Framework (Swift 6+)

Prefer the new `@Test` / `#expect` API over XCTest for new code.

```swift
import Testing

struct MathTests {
    @Test func addition() {
        #expect(2 + 2 == 4)
    }

    @Test("Subtraction is inverse of addition")
    func subtraction() {
        let result = 10 - 3
        #expect(result == 7)
    }

    @Test func throws() async throws {
        #expect(throws: ValidationError.self) {
            try validate("")
        }
    }
}
```

## Parameterized Tests

```swift
@Test("Validates email addresses", arguments: [
    ("user@example.com", true),
    ("invalid-email",    false),
    ("a@b.c",           true),
    ("@noDomain.com",   false),
])
func emailValidation(email: String, isValid: Bool) {
    #expect(validateEmail(email) == isValid)
}
```

## Suites and Tags

```swift
@Suite("User registration")
struct RegistrationTests {
    @Test(.tags(.critical)) func successfulRegistration() async throws { ... }
    @Test(.tags(.edge))     func duplicateEmail() async throws { ... }
    @Test(.disabled("Flaky on CI — see #1234")) func raceCondition() { ... }
}

extension Tag {
    @Tag static var critical: Self
    @Tag static var edge: Self
}
```

## Async Tests

```swift
@Test func loadsUserAsync() async throws {
    let service = UserService(client: MockHTTPClient())
    let user = try await service.load(id: UUID())
    #expect(user.name == "Alice")
}
```

## XCTest Patterns (legacy / UIKit integration tests)

```swift
import XCTest

final class CartTests: XCTestCase {
    var sut: Cart!

    override func setUp() {
        super.setUp()
        sut = Cart()
    }

    override func tearDown() {
        sut = nil
        super.tearDown()
    }

    func testAddItem_increasesCount() {
        let item = Item(name: "Book", price: 9.99)
        sut.add(item)
        XCTAssertEqual(sut.items.count, 1)
    }

    func testAsync() async throws {
        let result = try await sut.checkout()
        XCTAssertTrue(result.success)
    }
}
```

## Protocol-Based Mocking

```swift
protocol HTTPClient: Sendable {
    func data(for request: URLRequest) async throws -> (Data, URLResponse)
}

struct MockHTTPClient: HTTPClient {
    var result: Result<Data, Error>

    func data(for request: URLRequest) async throws -> (Data, URLResponse) {
        let data = try result.get()
        let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!
        return (data, response)
    }
}

// Usage in test
let mock = MockHTTPClient(result: .success(encodedUser))
let service = UserService(client: mock)
```

## Test Structure (AAA)

```swift
@Test func createsOrderWithCorrectTotal() {
    // Arrange
    let items = [Item(price: 10), Item(price: 20)]
    let cart = Cart(items: items)

    // Act
    let order = cart.checkout(tax: 0.1)

    // Assert
    #expect(order.subtotal == 30)
    #expect(order.tax == 3)
    #expect(order.total == 33)
}
```

## Snapshot Testing (third-party)

```swift
// Using swift-snapshot-testing
import SnapshotTesting

class ViewSnapshotTests: XCTestCase {
    func testButtonAppearance() {
        let button = PrimaryButton(title: "Buy Now")
        assertSnapshot(of: button, as: .image(on: .iPhone13))
    }
}
```

## Common Anti-Patterns

- **Testing implementation details** — test behavior and outputs, not private methods
- **Shared mutable test state** — reset in `setUp`/`tearDown` or use isolated instances
- **`sleep` in async tests** — use `await` with proper async APIs instead
- **Skipping tearDown** — always clean up to avoid test pollution
- **XCTAssert in new code** — prefer `#expect` with the Swift Testing framework

