25
Backend
04
Security
08
iOS
07
Infra

Test and preview iOS services over a mocked Apollo network transport

ADR-0004 ACCEPTED · 2025-07-10
Test and preview iOS services over a mocked Apollo network transport

Absorbs ADR-0050.

Decision

Test iOS behaviour with unit tests that run services against a real ApolloClient whose NetworkTransport is a mock. The tests cover business logic, state and the data that flows through services and the GraphQL client. They assert on service state directly and never drive the UI.

Feed Xcode Previews from the same seam. A preview's services sit on an Apollo client with an in-memory cache and a mock transport, with their state set up front, so they return at once with realistic data and never reach the network. Shared setups are built once with SwiftUI's PreviewModifier and injected through the environment, one named preview trait per common state, such as a loaded trip or a signed-out user.

Why

Tests are cheap for us to generate, so the hard question is what to test where. The usual iOS stack has four overlapping layers: unit, integration, UI automation and snapshot. One bug gets caught four ways and every layer's tests need upkeep. We followed Point-Free's "Testing SwiftUI" argument for fewer layers with distinct jobs. The original decision paired these unit tests with device snapshot tests for layout, and that snapshot layer has since been withdrawn.

A mock transport keeps the Apollo client, its normalized store and the service code inside the test and replaces only the network. A failure then points at our logic or at how it uses the cache.

Previews need the same substitution for a different reason. Views are backed by environment services (ADR-0003): a view starts a fetch when it appears and watches the service's state. Xcode Previews cannot reach the backend and hang on async work that never completes. Replacing the transport fixes the canvas without changing how any view gets its data.

Rejected alternatives

  • UI automation such as XCUITest as the behaviour layer. It is slow, couples tests to how views are built, and re-tests logic that unit tests already reach.
  • A protocol per service with a hand-written fake. Every service then needs a protocol and a fake written against it, and the fake bypasses the Apollo store, where much of the behaviour under test lives.
  • Views that take their data as parameters so previews can pass it in. It bends the architecture to suit the canvas and reintroduces the copies of cached data that ADR-0003 forbids.

Consequences

The suite runs fast and a failure points at logic. Journeys across several screens fall outside this layer; ADR-0069 covers them with end-to-end flows. Previews render instantly, a new preview state costs one more PreviewModifier, and the preview setup grows with every property a service gains.