Files
honeyDueKMP/iosApp/HoneyDueUITests/TESTING.md
T
Trey T 7cdd88b11a docs: TESTING.md + TEST_RULES.md for the isolation/domain model
Document the two-target layout, per-test Kratos account isolation, the
seed-before-login precondition rules (requiresResidence /
seedAccountPreconditions), how to run the phased runner, and how to add a suite.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 16:37:05 -05:00

3.5 KiB
Raw Blame History

HoneyDue iOS Tests — How it works

Two test targets:

Target Kind What it covers Speed
HoneyDueUITests XCUITest Drives the real app via accessibility IDs. Organized by domain. slow (per-test app launch + account)
HoneyDueAPITests standalone unit-test Pure-API/contract tests (no UI, no app launch). seconds

Running

cd iosApp

# Full run: Smoke gate -> Seed -> API -> Parallel UI (8 workers) -> Sweep
./run_ui_tests.sh

# Variants
./run_ui_tests.sh "iPhone Air" 6   # device + worker count
./run_ui_tests.sh --smoke          # just the smoke gate
./run_ui_tests.sh --only-api        # just the fast API target
./run_ui_tests.sh --only-parallel   # just the UI suites

The parallel phase runs the whole UI target minus the four phase-managed suites (SmokeUITests, AppLaunchUITests, AAA_SeedTests, SuiteZZ_CleanupTests) via -skip-testing. New suites are picked up automatically — there is no hand-maintained include list to forget to update.

Per-test account isolation (the core idea)

Every UI test that needs to be logged in subclasses AuthenticatedUITestCase, which in setUp:

  1. mints a unique, pre-verified Kratos identityuit_<domain>_<uuid>@test.honeydue.local (see Core/Fixtures/TestAccount.swift),
  2. logs the app in as that account,
  3. exposes session / cleaner / account for seeding under its own token,

and in tearDown calls account.delete(), which cascades all of the account's data and clears the Kratos identity in one call.

No test shares state with any other, so suites run in parallel at high worker counts with zero data races. (This replaced the old shared-testuser model that capped the suite at 2 workers and forced Suite6 to run isolated.)

Seeding data the UI must SEE

A fresh account is empty at login. Anything you seed via API after login is invisible to the app until a manual refresh. So:

  • Creating a thing through the UI → no setup needed (but note: task and document creation are gated on a residence existing — set override var requiresResidence: Bool { true }, which seeds one before login and exposes it as seededResidence).

  • Viewing / editing / deleting an existing thing → seed it before login:

    override func seedAccountPreconditions(_ account: TestAccount) {
        super.seedAccountPreconditions(account)         // seeds seededResidence if requiresResidence
        let r = seededResidence ?? account.seedResidence()
        myTask = account.seedTask(residenceId: r.id, title: "Edit me")
    }
    

Adding a suite

  1. Pick the domain folder (Task/, Residence/, …). The folder is an Xcode synchronized group, so a new .swift file is compiled into the target automatically — no .pbxproj editing.
  2. Name it <Domain><Aspect>UITests; subclass BaseUITestCase (logged-out surface: login/registration/onboarding) or AuthenticatedUITestCase (logged-in feature work).
  3. Use accessibility IDs from iosApp/Helpers/AccessibilityIdentifiers.swift (shared with the app — the single source of truth).
  4. It runs automatically in the parallel phase. Done.

For a pure-API test (no UI): add it to HoneyDueAPITests/ instead — it'll run in the fast API phase.

Known trade-off

Per-test account creation costs ~12s of setup (Kratos create + login). For a big suite that adds up; if a full run drags, the planned mitigation is a recycled account pool keyed by worker id. Correctness-first today.