Make your app testable¶
When Skeg records a step, it names the element by a unique id if there is one, else by a unique label, else by a screen point. On replay, a step that names an element looks for that element only. If the element is gone, the step fails instead of tapping blind.
- A step that names a stable id survives copy changes, translations and layout changes.
- A step that names visible text fails when the text changes.
- A step recorded as a point taps a screen position. It breaks on any other screen size.
Give every interactive element a unique id¶
| Platform | Set this | Skeg selector |
|---|---|---|
| Android (Views) | android:id="@+id/checkout_button" |
id=checkout_button |
| Android (Jetpack Compose) | Modifier.testTag("checkout_button"), plus testTagsAsResourceId = true once near the root |
id=checkout_button |
| iOS (UIKit / SwiftUI) | accessibilityIdentifier = "checkout_button" / .accessibilityIdentifier("checkout_button") |
id=checkout_button |
| macOS | accessibilityIdentifier (shows as AXIdentifier) |
id=checkout_button |
Compose hides test tags from UI automation by default. Enable them once, high in the tree:
Scaffold(
modifier = Modifier.semantics { testTagsAsResourceId = true },
) { /* … */ }
See the Android guide on Compose and UiAutomator interop.
Rules that keep recorded plans replayable:
- Unique on screen. If two elements share an id or a label, Skeg records a screen point
instead, which does not replay on other screen sizes. Skeg reports these steps as
point. - Not obfuscated. R8 resource shrinking can rename ids (
0_resource_name_obfuscated). Test a build where ids keep their names. - Stable across builds. Do not generate ids from list positions or random values.
Start every run from a known state¶
A plan that assumes an empty cart fails when the last run left one item in it.
- Reset the app. Save plans with
reset_app: true(MCPpromote_goal_runorsave_recorded_plan). The plan then starts with a launch step that wipes the app's data and relaunches it. On iOS it also clears the simulator keychain. macOS does not support the reset: a reset step on a macOS plan fails every replay, so never save a macOS plan withreset_app. - Seed with launch arguments. A launch step can pass arguments as
with.arg.<name>. Android receives them as intent extras, iOS as launch arguments. Read them in a debug build to load fixtures, for examplearg.seed: empty_cart. - Seed the backend. A step with action
server(orcurl) runs onecurlcommand without a shell, for example to create a test user before the UI steps. Onlyhttp/httpsURLs are allowed, and options that read or write local files are refused.
Note
The plan editor does not edit step options (with.*) yet. Plans get them from MCP recording
and promotion.
Keep test data out of the plan¶
- Create an environment in Skeg (Envs) with variables such as
usernameandpassword. - Mark credentials secret. Skeg masks secret values in reports and drops screenshots after a secret is typed.
- Reference them in steps as
${username}. A plan's default environment fills the tokens on every replay.
Use test accounts only. Never put production credentials in an environment.
Ship a test build¶
Skeg does not build your app. Install the build with your own toolchain, or upload it in
Setup so the runner installs it before each run: an .apk for Android, a zipped .app for
the iOS Simulator.