--- name: swift-testing description: > Write, review, or migrate unit tests using Swift Testing (@Test, @Suite, #expect, #require). Use when writing new tests, converting existing Quick+Nimble specs to Swift Testing, reviewing test quality, debugging flaky tests, or improving test structure in this iOS template project. Triggers on: "write test", "add test", "test this", "migrate test", "convert spec", "Swift Testing". --- # Swift Testing ## Overview Write, review, migrate, and debug tests using Swift Testing for this iOS template project. This project uses **Quick + Nimble** (`*Spec.swift`) as the legacy framework and **Swift Testing** (`*Tests.swift`) for all new tests. ## Agent Behavior Contract 1. **Swift Testing for all new unit/integration tests.** Retain Quick+Nimble only for existing specs; do not migrate unless asked. 2. Use `#expect` as the default assertion; use `#require` only when subsequent lines depend on a prerequisite value. 3. Design tests to run safely in parallel by default. Fix shared state before reaching for `.serialized`. 4. Use inline `private struct` / `private actor` stubs conforming to domain protocols — no generated mocks. 5. Use `actor` stubs when the protocol requires `async` or has mutable state; use `struct` stubs otherwise. 6. Prefer parameterized tests (`@Test(arguments:)`) over duplicated test methods. 7. Use traits (`.enabled`, `.disabled`, `.timeLimit`, `.bug`, `.tags`) instead of ad-hoc comments. 8. Import `Testing` only in test targets, never in app/library targets. 9. Keep tests deterministic — no real network calls. Use `NetworkStubber` (OHHTTPStubs) for network-level tests. ## Quick Triage Before writing or reviewing tests, clarify: 1. **New or migration?** Writing fresh tests vs converting an existing `*Spec.swift`. 2. **Which module?** App (`{PROJECT_NAME}Tests/`), Data (`Modules/Data/Tests/`), Domain (`Modules/Domain/Tests/`), or Model (`Modules/Model/Tests/`). 3. **Async?** Does the code under test use `async/await`? 4. **Shared resources?** Database, network, file system, or singleton state involved? ## File Naming & Placement | Framework | Suffix | Placement | |-----------|--------|-----------| | Swift Testing | `*Tests.swift` | `Tests/Sources/` mirroring production path (e.g. `Repositories/`, `Entities/`) | | Quick + Nimble | `*Spec.swift` | `Tests/Sources/Specs/` | ## Canonical Pattern (this project) ```swift import Testing @testable import Data import Domain @Suite("DefaultRemoteConfigRepository") struct DefaultRemoteConfigRepositoryTests { @Test("returns a typed stored value") func returnsATypedStoredValue() async { let booleanKey = RemoteConfigKey(name: "feature_enabled", defaultValue: false) let repository = DefaultRemoteConfigRepository( source: StubRemoteConfigSource(values: [ booleanKey.name: .string("true") ]) ) let value = await repository.value(for: booleanKey) #expect(value == true) } } private actor StubRemoteConfigSource: RemoteConfigSource { private let values: [String: RemoteConfigStoredValue] init(values: [String: RemoteConfigStoredValue] = [:]) { self.values = values } func refresh() async throws {} func value(forKey key: String) async -> RemoteConfigStoredValue? { values[key] } } ``` Key observations: - `struct` suite, not `class` - `@Suite("DisplayName")` with human-readable name matching the type under test - `@Test("description")` with lowercase sentence describing behavior - Function name is camelCase matching the description (no `test` prefix) - Arrange / Act / Assert with blank line separations - Inline `private actor` stub at bottom of file (actor because protocol has `async`) - `@testable import` for the module under test; regular `import` for its dependencies ## Routing to References Load the appropriate reference file based on the task: | Task | Reference | |------|-----------| | Writing new tests, suite structure, naming | `references/fundamentals.md` | | Assertions, error checking, known issues | `references/expectations.md` | | Traits, tags, conditional execution | `references/traits-and-tags.md` | | Data-driven / parameterized tests | `references/parameterized-testing.md` | | Async/await, callbacks, confirmations | `references/async-testing.md` | | Creating stubs, fakes, network mocks | `references/test-doubles.md` | ## Verification Checklist Before finishing, confirm: - [ ] Each test verifies one clear behavior - [ ] `#require` is used for prerequisites; `#expect` for everything else - [ ] Parameterized tests replace duplicated test methods - [ ] Tests are parallel-safe (no shared mutable state) - [ ] Async tests use `async` functions directly (no `Task.sleep` hacks) - [ ] Stubs are `private struct` or `private actor` at bottom of file - [ ] File is named `*Tests.swift` and placed in the correct module test directory - [ ] No real network calls — `NetworkStubber` or protocol stubs used