Testing and Quality - awizemann/herald GitHub Wiki
Testing and Quality
Herald uses Swift Testing (not XCTest). Tests are organized by feature, with injected fakes for all dependencies.
Test structure
HeraldTests/— App-level tests (view models, compose, notifications, analytics)HeraldKit/Tests/HeraldKitTests/— Kit-level tests (sync, auth, API, drafts)
Test suites use the @Suite / @Test macro syntax. Each test file is a struct with test methods.
Testing conventions
Swift Testing conventions:
- Swift Testing only (
@Suite/@Test/#expect); no XCTest - Protocol-oriented fakes (injected via
nonisolated protocols) - Protocols meant to be adopted by fakes are declared
nonisolated protocol … : Sendable - No mocking frameworks; hand-written fakes
- Assertions use
#expect(condition), notXCTAssertTrue
Common fakes
FakeMailAPIClient(HeraldKit/Tests/HeraldKitTests/Sync/FakeMailAPIClient.swift:9) — Records calls and returns fixturesFakeServer(HeraldKit/Tests/HeraldKitTests/Support/FakeServer.swift:55) — URLProtocol-based fake HTTP serverFakeTokenProvider(HeraldKit/Tests/HeraldKitTests/Support/FakeTokenProvider.swift:8) — In-memory account and token storeFakeOutbox(HeraldTests/ComposeViewModelTests.swift:10) — Records sent messages
Running tests
From Xcode, select the test target and press Cmd+U.
From the command line:
xcodebuild test -project Herald.xcodeproj -scheme Herald -destination 'platform=macOS' -derivedDataPath DerivedData
The Herald scheme runs both HeraldTests and HeraldKitTests (roughly 370 tests today). Analytics tests run inside the sandboxed host, so they use throwaway app ids and temp queue directories — never the production UserDefaults suite.
UI Tests — The herald-ui-tests scheme exercises the full app with keyboard and mouse automation. Do NOT run UI tests automatically or in CI; they take over the screen and keyboard. Run scripts/ui-tests.sh only with explicit approval from the project owner, as it locks the machine for the duration of the test.
Discriminating tests
Tests are "discriminating" — they exercise a real feature end-to-end using fakes at the boundary, not mocks scattered throughout. For example:
- An auth test uses a real
AuthCoordinatorwith aFakeAuthorizationPresenter - A sync test uses a real
SyncEnginewith aFakeMailAPIClient - A view-model test uses a real
MailViewModelwith aFakeMailAPIClientandFakeOutbox
This style catches integration bugs that mocks would hide.
Last updated: 2026-09-29 — testing and quality runbook; added UI test warning and runbook reference