Testing¶
PreviewLocationTracker¶
For Composable tests and Compose previews, Dhruva ships PreviewLocationTracker , a fixed LocationTracker you control directly:
@Test
fun showsCoordinatesAfterLocate() = runComposeUiTest {
val tracker = PreviewLocationTracker(
fixed = Location(latitude = -33.86, longitude = 151.21, timestamp = 0),
)
setContent { LocationLabel(tracker = tracker) }
onNodeWithText("-33.86, 151.21").assertIsDisplayed()
}
For Composables that get the tracker from rememberLocationTracker, factor it out as a parameter:
@Composable
fun LocationLabel(
tracker: LocationTracker = rememberLocationTracker(),
) { /* ... */ }
Streaming tests¶
PreviewLocationTracker.trackingFlow lets you control the stream:
val fixes = listOf(
Location(latitude = -33.86, longitude = 151.21, timestamp = 0),
Location(latitude = -33.87, longitude = 151.22, timestamp = 1_000),
Location(latitude = -33.88, longitude = 151.23, timestamp = 2_000),
)
val tracker = PreviewLocationTracker(trackingFlow = fixes.asFlow())
Manual integration tests¶
The behaviors of FusedLocationProviderClient and CLLocationManager are too stateful to mock meaningfully. Use the sample apps as a manual-test rig and run through these scenarios before each release:
| Scenario | How to reproduce |
|---|---|
| One-shot, warm cache | App in foreground, tap "Get Once", verify sub-second response. |
| One-shot, cold cache | After clearing app data, tap "Get Once", verify fresh fix arrives within timeoutMs. |
| Streaming | Tap "Stream", verify updates roll in as you walk around. |
| Permission missing | Deny permission, tap "Get Once", verify PermissionDenied. |
| Location disabled | Toggle device location off, tap "Get Once", verify LocationDisabled. |
| Mid-stream revocation | Start streaming, revoke permission via Settings, verify the collector terminates with PermissionDenied. |
Unit tests for Location validation¶
Location validates lat/lng in its init block:
@Test
fun rejectsLatitudeAbove90() {
assertFailsWith<IllegalArgumentException> {
Location(latitude = 90.5, longitude = 0.0, timestamp = 0)
}
}
These run against the state module; no platform setup required.