| name | testing |
| description | Decision framework for choosing between progressive exploration, unit tests, and smoke tests. Does NOT describe how to write them โ see progressive-exploration, unit-test, smoke-test skills for that. |
| license | MIT |
| compatibility | opencode |
| metadata | {"author":"https://github.com/TT432","version":"1.0.0"} |
The Three Verification Layers
| Layer | Tool | Trigger | What it catches |
|---|
| Progressive exploration | AI debug HTTP endpoint | Interactive, step by step | Unknown runtime state, unexpected screens, "what actually happens?" |
| Unit tests | JUnit 5 + Gradle | CI / pre-commit / on-demand | Structural invariants, boundary ownership, codec round-trips, null safety |
| Smoke tests | ClientSmoke framework | Gradle run configuration | Visual correctness, full lifecycle integration, render output verification |
They form a funnel, not a pyramid: exploration is broadest (any question), unit tests are narrowest (specific assertions), smoke tests are deepest (real client rendering).
The Funnel Model
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Progressive Exploration โ โ widest: ask anything at runtime
โ "Is this screen even open?"โ
โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโ
โ narrows to a specific invariant
โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโ
โ Unit Tests โ โ narrowest: assert one thing, fast
โ "This import must not exist"โ
โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโ
โ narrows to visual behavior
โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโ
โ Smoke Tests โ โ deepest: real rendering, slow
โ "This region has the right โ
โ pixel color" โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Decision Flow
When you encounter a problem or need to verify something:
- Is the answer already in code? โ Read it. Don't run anything.
- Is the answer about runtime state or behavior? โ Progressive exploration first. Don't guess.
- Now you have a reproducible invariant. โ Write a unit test to lock it in.
- Does it involve visual output or full client lifecycle? โ Add a clientSmoke test.
Why All Three Matter
- Unit tests alone tell you code is structurally correct, but not whether runtime state matches expectations. A Mixin may apply but produce wrong behavior.
- Smoke tests alone verify integration but are slow and give binary pass/fail โ they don't help explore why something broke.
- Exploration alone is ephemeral โ it can't regression-test yesterday's discovery.
A single bug typically requires all three: explore to find the problem โ unit test to lock it โ smoke test to verify visually.
Anti-patterns
- Running the client to answer a question that reading the source code can answer
- Writing a smoke test for something a unit test can catch
- Guessing runtime state instead of probing it with the debug endpoint
- Leaving a runtime discovery undocumented (no follow-up unit test)
- Trusting Gradle
FROM-CACHE or UP-TO-DATE โ if a test task shows no tests executed (0 tests, 0 failed, 0 skipped) or all tasks are FROM-CACHE, the tests did NOT run. Clear the project build directory and re-run.
- Using
--no-build-cache โ never pass this to Gradle. It forces a full rebuild of MC Forge artifacts taking 30+ minutes. Clear only the affected module's build/ directory and the configuration cache (.gradle/configuration-cache).