-
Notifications
You must be signed in to change notification settings - Fork 18
Swift Testing
team-nimblehq edited this page Apr 10, 2026
·
1 revision
New unit and integration tests use Swift Testing (import Testing). Existing Quick + Nimble specs (*Spec.swift) are retained as-is and migrated only on request.
| Framework | File suffix | Location |
|---|---|---|
| Swift Testing | *Tests.swift |
Tests/Sources/ mirroring production path |
| Quick + Nimble | *Spec.swift |
Tests/Sources/Specs/ |
Mirror the production source path for the module under test:
| Module | Test directory |
|---|---|
| App | {PROJECT_NAME}Tests/Sources/ |
| Data | Modules/Data/Tests/Sources/ |
| Domain | Modules/Domain/Tests/Sources/ |
| Model | Modules/Model/Tests/Sources/ |
| Element | Convention | Example |
|---|---|---|
| File | <TypeUnderTest>Tests.swift |
DefaultFeatureFlagRepositoryTests.swift |
| Suite type | <TypeUnderTest>Tests |
struct DefaultFeatureFlagRepositoryTests |
| Suite display name | @Suite("<TypeUnderTest>") |
@Suite("DefaultFeatureFlagRepository") |
| Test display name | @Test("lowercase behavior sentence") |
@Test("falls back to the flag default value") |
| Test function | camelCase matching display name | func fallsBackToTheFlagDefaultValue() |
import Testing
@testable import Data
import Domain
@Suite("DefaultRemoteConfigRepository")
struct DefaultRemoteConfigRepositoryTests {
@Test("returns a typed stored value")
func returnsATypedStoredValue() async {
let key = RemoteConfigKey(name: "feature_enabled", defaultValue: false)
let repository = DefaultRemoteConfigRepository(
source: StubRemoteConfigSource(values: [key.name: .string("true")])
)
let value = await repository.value(for: key)
#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] }
}- Use
#expectby default; use#requireonly when subsequent lines depend on a non-nil or non-throwing prerequisite. - Prefer
structsuites — each test gets a fresh instance, preventing shared state. - Use
init()for setup;deiniton aclasssuite for teardown (rare). - Tests run in parallel by default. Mark a suite
.serializedonly when it accesses global state (e.g.NetworkStubber). - No real network calls — use
NetworkStubber(OHHTTPStubs) or inline protocol stubs. - Use
actorstubs when the protocol hasasyncmembers or mutable state; usestructotherwise.
The /swift-testing Claude Code skill provides detailed references for assertions, traits, parameterized tests, async testing, test doubles, and Quick + Nimble migration:
skills/swift-testing/
├── SKILL.md
└── references/
├── fundamentals.md
├── expectations.md
├── traits-and-tags.md
├── parameterized-testing.md
├── async-testing.md
├── test-doubles.md
└── migration-from-quick-nimble.md
This project is maintained and funded by Nimble.