Skip to content

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 (MCP promote_goal_run or save_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 with reset_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 example arg.seed: empty_cart.
  • Seed the backend. A step with action server (or curl) runs one curl command without a shell, for example to create a test user before the UI steps. Only http/https URLs 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

  1. Create an environment in Skeg (Envs) with variables such as username and password.
  2. Mark credentials secret. Skeg masks secret values in reports and drops screenshots after a secret is typed.
  3. 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.