agentsclimarketplace

Ui testing patterns

Skill almasumdev/awesome-mobile-testing-agent-skills/.github/skills/ui/ui-testing-patterns

Compare and apply UI test tooling across mobile — Espresso, XCUITest, flutter_test / integration_test, Detox, and Maestro. Use when deciding which UI test layer fits a feature and how to structure selectors, waits, and page objects.From its SKILL.md

Install
npx -y skills add almasumdev/awesome-mobile-testing-agent-skills --skill ui-testing-patterns

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

5.4 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it

UI Testing Patterns Across Mobile

Instructions

UI tests are the most expensive layer to run and to maintain. Pick the lightest tool that still covers the risk, write them in a page-object / screen-robot style, and keep selectors stable.

1. Pick the Right Tool

ToolScopeRuns OnBest For
Espresso / Compose UI testAndroid, in-processEmulator / deviceSingle-screen behavior, fast iteration
Robolectric + ComposeAndroid, JVMHost JVMCompose component tests without an emulator
XCUITestiOS, out-of-processSimulator / deviceEnd-to-end iOS flows
ViewInspector / SwiftUI Previews + snapshotiOS, in-processSimulatorSwiftUI component tests
flutter_test (widget tests)FlutterHost Dart VMWidget-level, fast
integration_testFlutterEmulator / deviceReal-device smoke
DetoxReact NativeEmulator / deviceFull RN E2E in JS
MaestroAny mobile appEmulator / device / realBlack-box YAML smoke flows, cross-platform

Rule of thumb: prefer in-process component tests (Compose, SwiftUI views, flutter_test, RN Testing Library) for 80 % of UI coverage; reserve out-of-process (XCUITest, Detox, Maestro) for smoke and critical paths.

2. Selector Strategy

Tests break most often because someone changed a string or a layout. Use stable, semantic selectors:

  • Android Views: R.id.* resource IDs.
  • Android Compose: Modifier.testTag("checkoutPayButton") + onNodeWithTag.
  • iOS UIKit: accessibilityIdentifier.
  • iOS SwiftUI: .accessibilityIdentifier("checkoutPayButton").
  • Flutter: Key('checkoutPayButton') / ValueKey.
  • React Native: testID="checkoutPayButton" + getByTestId.

Never select by visible text unless the text is the thing under test (e.g., a localization check).

3. Page-Object / Screen-Robot Pattern

Wrap each screen in a test-only API so tests read like business steps, not framework calls.

Kotlin + Compose:

class CheckoutRobot(private val rule: ComposeTestRule) {
    fun enterCard(number: String) = apply {
        rule.onNodeWithTag("cardNumber").performTextInput(number)
    }
    fun tapPay() = apply { rule.onNodeWithTag("pay").performClick() }
    fun assertSuccess() = apply { rule.onNodeWithTag("success").assertIsDisplayed() }
}

@Test fun happyPath() = composeRule.run {
    CheckoutRobot(this).enterCard("4242...").tapPay().assertSuccess()
}

Swift + XCUITest:

struct CheckoutScreen {
    let app: XCUIApplication
    @discardableResult func enterCard(_ s: String) -> Self {
        app.textFields["cardNumber"].tap()
        app.textFields["cardNumber"].typeText(s); return self
    }
    @discardableResult func tapPay() -> Self { app.buttons["pay"].tap(); return self }
    func assertSuccess() { XCTAssertTrue(app.staticTexts["success"].waitForExistence(timeout: 5)) }
}

Dart + flutter_test:

class CheckoutRobot {
  CheckoutRobot(this.t);
  final WidgetTester t;
  Future<void> enterCard(String s) async {
    await t.enterText(find.byKey(const Key('cardNumber')), s);
  }
  Future<void> tapPay() async => t.tap(find.byKey(const Key('pay')));
  void assertSuccess() => expect(find.byKey(const Key('success')), findsOneWidget);
}

4. Waits — No sleep

Use the framework's own "wait until condition" primitive:

  • Espresso — IdlingResource plus sensible onView(...).check(matches(...)) which blocks on idling.
  • Compose — waitForIdle, waitUntil { composeRule.onAllNodesWithTag(...).fetchSemanticsNodes().isNotEmpty() }.
  • XCUITest — waitForExistence(timeout:), XCTNSPredicateExpectation.
  • Flutter — pumpAndSettle() (scoped timeout) or an explicit pump loop with a deadline.
  • Detox — waitFor(element(...)).toBeVisible().withTimeout(5000).

Never Thread.sleep / Task.sleep / await Future.delayed in UI tests. Waits must be condition-based.

5. Isolate from the World

Swap the network layer with a recorded or mocked server for the whole suite; swap the clock; seed a known user in local storage during @Before. UI tests must not depend on staging.

6. Cross-Platform Comparison

  • Detox vs Maestro: Detox is JS-first and lives with the RN codebase; it covers complex flows with code. Maestro is YAML-first and works on any stack — prefer Maestro for smoke, Detox for deep RN-specific flows.
  • Flutter integration_test vs Maestro: integration_test gives Dart-level access and test semantics; Maestro is more robust on CI device farms and produces cleaner videos.

7. Failure Artifacts

Every UI test failure must produce: screenshot, view hierarchy / semantics dump, logs (logcat / os_log / Flutter debugDumpApp). Make that automatic in a @AfterEach / tearDown hook and upload from CI.

8. Checklist

  • Selectors are test IDs / keys / accessibility identifiers — never raw visible text.
  • Screens are wrapped in page objects / robots.
  • No sleep-based waits anywhere.
  • Network, time, and storage are isolated from real services.
  • Failure produces screenshot + hierarchy + logs as CI artifacts.
  • Each flow runs in under ~60 s; if longer, split it.
  • Smoke vs regression suites are tagged and run on different cadences.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,782. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.