test(compose): enable Compose UI unit testing via Robolectric (shrink JaCoCo exclusions, then re-ratchet the floor) #373

Closed
opened 2026-07-06 19:45:50 +00:00 by JMR-dev · 1 comment
JMR-dev commented 2026-07-06 19:45:50 +00:00 (Migrated from github.com)

Context

Jetpack Compose UI (screens, gates) is currently JVM-unreachable, so it's excluded from JaCoCo JVM coverage via jacocoNonJvmTestableSurface in app/build.gradle.kts (each render-only file named individually) and only exercised by instrumented androidTest. That caps the coverage metric and leaves UI logic un-unit-tested.

Goal

Enable Robolectric-backed JVM unit testing of Compose UI. Robolectric runs the Android framework on the JVM; with the Compose test APIs (createComposeRule()) composables can be unit-tested without an emulator — bringing that UI into the JVM-testable + JaCoCo-counted surface, so those exclusions shrink and the coverage floor can be re-ratcheted up once the tests are stable.

Approach (this is the umbrella)

An Opus spike scopes it: verify Robolectric + Compose works on this stack (AGP 9.2 built-in Kotlin, Kotlin 2.4.0, Compose, JDK 21) with a runnable PoC, inventory the excluded UI, then decide:

  • One session → implement (infra + PoC + convert excluded UI to Robolectric JVM tests + drop those exclusions) + PR.
  • Too big → scoped sub-tickets (infra + per-area conversion batches + a final "re-ratchet the floor"), one Opus agent per ticket.

The JaCoCo floor re-ratchet is the FINAL step, only after the Compose tests are proven stable — NOT in the conversion PR(s).

Relates to #323 (coverage gate), #367 (excluded CacheEncryptionGate), and the "no mockk in instrumented tests" gotcha (Robolectric may sidestep it).

## Context Jetpack Compose UI (screens, gates) is currently **JVM-unreachable**, so it's excluded from JaCoCo JVM coverage via `jacocoNonJvmTestableSurface` in `app/build.gradle.kts` (each render-only file named individually) and only exercised by instrumented `androidTest`. That caps the coverage metric and leaves UI logic un-unit-tested. ## Goal Enable **Robolectric**-backed JVM unit testing of Compose UI. Robolectric runs the Android framework on the JVM; with the Compose test APIs (`createComposeRule()`) composables can be unit-tested without an emulator — bringing that UI into the JVM-testable + JaCoCo-counted surface, so those exclusions shrink and the coverage floor can be **re-ratcheted up** once the tests are stable. ## Approach (this is the umbrella) An Opus spike scopes it: verify Robolectric + Compose works on this stack (AGP 9.2 built-in Kotlin, Kotlin 2.4.0, Compose, JDK 21) with a runnable PoC, inventory the excluded UI, then decide: - **One session** → implement (infra + PoC + convert excluded UI to Robolectric JVM tests + drop those exclusions) + PR. - **Too big** → scoped sub-tickets (infra + per-area conversion batches + a final "re-ratchet the floor"), one Opus agent per ticket. The **JaCoCo floor re-ratchet is the FINAL step**, only after the Compose tests are proven stable — NOT in the conversion PR(s). Relates to #323 (coverage gate), #367 (excluded `CacheEncryptionGate`), and the "no mockk<Context> in instrumented tests" gotcha (Robolectric may sidestep it).
JMR-dev commented 2026-07-08 23:13:32 +00:00 (Migrated from github.com)

Umbrella complete — closing. All decomposed work has landed:

  • #375 — Robolectric + Compose JVM testing infra + PoC (merged)
  • #376–#384 — Compose UI Robolectric conversion batches 1–9 (all Done)
  • #385 — v1→v2 \createComposeRule/\createAndroidComposeRule\ migration (Done, via #458)
  • #386 — final JaCoCo line-coverage floor re-ratchet (Done)

The umbrella was left OPEN/In-progress after the last sub-ticket landed (orphaned by an earlier session crash). No work remains; closing as completed.

Umbrella complete — closing. All decomposed work has landed: - **#375** — Robolectric + Compose JVM testing infra + PoC (merged) - **#376–#384** — Compose UI Robolectric conversion batches 1–9 (all Done) - **#385** — v1→v2 \createComposeRule\/\createAndroidComposeRule\ migration (Done, via #458) - **#386** — final JaCoCo line-coverage floor re-ratchet (Done) The umbrella was left OPEN/In-progress after the last sub-ticket landed (orphaned by an earlier session crash). No work remains; closing as completed.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: JMR-dev/LibreMail#373