04
Product
16
Backend
09
Auth
12
iOS
07
Infra
02
Real-Time
ADR-0004 ACCEPTED · 2025-07-10
Adopt two-layer approach to iOS testing: device snapshots for layouts, unit tests for behavior

Context

We want good coverage so we can move fast, but not tests that overlap and create maintenance work. LLMs make generating tests trivial now, so the problem isn't writing enough tests — it's deciding what to test where, so you don't end up with four layers all catching the same bug differently.

Point-Free's "Testing SwiftUI" argues for two distinct layers rather than the typical four (unit, integration, UI automation, snapshot). That matched what we needed.

Integration tests would be ideal but the iOS story for them isn't great — running end-to-end tests through the Xcode simulator is clunky compared to web tooling like Cuprite. Rather than adopt something brittle, we left that layer out for now.

Decision

Two layers, nothing else:

Unit tests for behavior — business logic, state management, data flow. Services, models, Apollo integration. Tested with XCTest and mocked dependencies. No UI interaction testing — test state directly instead.

Snapshot tests for layout — visual regression across device configurations (iPhone SE through iPad Pro 12.9"). Tested with swift-snapshot-testing. Captures static layouts, not animations or loading states.

Consequences

Fast test execution with no UI automation overhead. Each layer has a single purpose, so failures point clearly at either a logic bug or a layout regression.

The gap is complex user journeys — multi-step flows need careful unit test design since there's no integration layer covering end-to-end scenarios. Snapshot reference images also need updating when UI changes are intentional.