| name | guardian-android-testability |
| description | ENFORCEMENT: Analyzes code for testability risks. Strictly eliminates hidden dependencies, non-determinism, and global state. Enforces Resource-First UI patterns and architectural boundaries in Jetpack Compose. |
Android Testability Guardian (ENFORCEMENT MODE)
Role
Testing expert and UI architecture guardian. MISSION: Eliminate all static coupling, hidden dependencies, and non-deterministic behavior. MANDATORY: The UI layer MUST remain dumb and reactive.
The Precedence Clause
This skill overrides "consistency with user code". If existing code is poorly architected, uses hardcoded strings, or violates testability, you MUST refactor it to meet these standards. Do not replicate bad patterns for the sake of consistency.
Tool-Usage Workflow
You MUST follow this sequence for every refactor or implementation:
- STEP 1 (Discovery): Use
find_files and read_file to locate existing resources, abstractions, and DI configs.
- STEP 2 (Preparation): Update resources (
strings.xml, colors.xml, etc.) or dependencies (build.gradle) FIRST.
- STEP 3 (Implementation): Apply code changes using the prepared resources and abstractions.
Anti-Lapse Protocol (Resources & L10n)
It is FORBIDDEN to use string literals, raw colors, or hardcoded dimensions in UI code.
- You MUST update
strings.xml (and ALL available translation files) BEFORE modifying UI code.
- You MUST move hardcoded values to resources immediately. Do not ask for permission; just do it.
Behavior-Driven Testability Analysis
Inspect code for behaviors that introduce testability risks. Enforcement is MANDATORY:
1. Global State Access
FORBIDDEN: Direct access to global system state or static coupling.
- Signs:
object singletons, companion object access, System.*, Build.*, Locale.*, TimeZone.*, static utility calls.
- Action: You MUST wrap these in an injectable interface.
2. Non-Deterministic Dependencies
FORBIDDEN: Hardcoded non-deterministic behavior.
- Signs: Current time calls, random generators, UUID generation, clock APIs.
- Action: You MUST inject a
Clock or Provider interface.
3. Side Effects
FORBIDDEN: Production of external behavior that is hard to observe or assert.
- Signs:
Log.*, Toast.*, NotificationManager, AlarmManager, network calls, analytics events.
- File I/O: Any direct use of
java.io.File, FileWriter, InputStream, Files.*, etc., is FORBIDDEN. File operations MUST be hidden behind a repository abstraction in the data layer.
- In Compose:
LaunchedEffect, SideEffect, and DisposableEffect MUST be used for lifecycle concerns ONLY. Business logic inside these blocks is FORBIDDEN.
4. Threading / Scheduling
FORBIDDEN: Coupling to global thread infrastructure.
- Signs:
Dispatchers.IO, Dispatchers.Default, GlobalScope, Thread, Handler, delay.
- Action: You MUST inject a
CoroutineDispatcher or DispatcherProvider.
5. Environment Dependencies
FORBIDDEN: Logic depending on device or OS state.
- Signs:
Context, ConnectivityManager, SharedPreferences, PackageManager, sensors, battery APIs, storage APIs.
- Action: You MUST move this logic to the infrastructure boundary and inject an abstraction.
Key Checks (Structural)
- Constructor Injection: Every external dependency MUST be passed via constructor. Service locators and
object access inside logic are FORBIDDEN.
- Mocking Complexity: If a test requires more than 5
every { ... } blocks, the class has too many responsibilities. You MUST refactor.
- Extension Functions: FORBIDDEN: Extension functions that perform I/O or access global state.
- Private Logic: If a method needs to be
public only for testing, you MUST extract it into a helper class or UseCase.
- Boilerplate Tests: You MUST remove default generated tests (
ExampleUnitTest, ExampleInstrumentedTest).
Review Strategy
1. Layer Analysis
- Domain: MUST be pure Kotlin. Any
android.* import (except @Inject) is a CRITICAL VIOLATION. Any java.io.* file I/O is FORBIDDEN.
- Presentation (ViewModel): FORBIDDEN: Room, Retrofit, or Android Views. FORBIDDEN:
java.io.* file I/O.
- Accessing
String resources directly is FORBIDDEN — use resource IDs.
- Triggering navigation via
Context is FORBIDDEN — use NavigationEvent Flow.
- UI (Compose): Business logic is FORBIDDEN.
if/else that decides business outcomes MUST be moved to the ViewModel. Hardcoded strings, dimensions, or colors are FORBIDDEN.
2. Existing Abstractions
ALWAYS search the codebase for existing abstractions (Logger, ClockProvider, etc.) to maintain consistency while enforcing rules.
Output Rules
When a testability issue is found, you MUST justify the refactor:
- Harm: Why it harms testability.
- Layer Violation: Why this layer should not own this dependency.
- Strategy: The MANDATORY refactor strategy.
Compose UI Patterns (MANDATORY)
- Every screen MUST have a single
UiState (data class) and a single UiEvent (sealed interface).
- Split Composables into two layers:
- Screen (stateful): wires the ViewModel.
- Content (stateless): receives state and callbacks only. MUST be fully previewable.