Skip to content
Back to skills

Rn Write Tests

ASecurity

Use to write tests for an Expo + RN app: Jest + React Native Testing Library for unit/integration (components, hooks, queries, mutations) and Maestro for end-to-end flows (sign-in, navigation, forms). Sets up the testing stack on first call (jest-expo preset, RNTL, jest config) and writes a focused test next to the source file. Triggers on: "write tests for X", "add e2e test", "two-user flow", "multi-user test", "test con due utenti", "shared state between users", "mock expo-notifications", "...

  • 6 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 19, 2026
developmentpythongojavabashreactnextjsnodeexpresstestinggit

Works with

  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 6 files and shows the line behind each finding

Scanned October 19, 2026

npx -y skills add lukedj78/dev-flow --skill rn-write-tests --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Rn Write Tests?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Rn Write Tests
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lukedj78-rn-write-tests/badge)](https://www.skillsdirectory.com/skills/lukedj78-rn-write-tests)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: rn-write-tests
description: 'Use to write tests for an Expo + RN app: Jest + React Native Testing Library for unit/integration (components, hooks, queries, mutations) and Maestro for end-to-end flows (sign-in, navigation, forms). Sets up the testing stack on first call (jest-expo preset, RNTL, jest config) and writes a focused test next to the source file. Triggers on: "write tests for X", "add e2e test", "two-user flow", "multi-user test", "test con due utenti", "shared state between users", "mock expo-notifications", "test this hook". Not for: choosing what to test (the user decides), running the existing test suite (just `npm test`), testing pure logic outside an Expo project, or a Next.js web app (write-tests).'
---

# rn-write-tests — Jest + RNTL + Maestro testing for Expo + RN

## Contract

See `references/contracts.md` (vendored from `dev-flow`). Key facts:
- Reads `<project-root>/.workflow/meta.json#stack.framework` — must be `"expo-rn"`.
- Adds dev dependencies + Jest config on first call (idempotent: detects existing setup).
- Writes test files under `<project-root>/__tests__/` (Jest convention) for unit/integration.
- Writes Maestro YAML flows under `<project-root>/.maestro/` for e2e.
- Does NOT modify `meta.json#phase`.

## When this skill applies

- User asks to write a test for a specific source file or flow.
- Orchestrator does NOT route here automatically — test writing is on-demand.

## Knowledge dependencies (read these first)

- `rn-fundamentals/SKILL.md` — TS strict, modern primitives the tests will see.
- `rn-data-fetching/references/patterns.md` — query/mutation patterns the tests must exercise.

## Workflow

### Step 1 — Verify preconditions

Read `.workflow/meta.json`. Abort if `stack.framework != "expo-rn"`.

### Step 2 — Detect / install the test stack

Check if `jest`, `jest-expo`, `@testing-library/react-native` are in `package.json`. If not:

```bash
npx expo install --dev \
  jest jest-expo \
  @testing-library/react-native@^14 \
  test-renderer@^1 \
  @types/jest -- --legacy-peer-deps

# jest-expo declares `@react-native/jest-preset` as a peer (58: `^0.88.0`), and that package is
# NOT in the SDK's bundledNativeModules — so `expo install` hands you npm latest, which breaks as
# soon as it is a minor ahead of the project's RN (SDK 57, 2026-09-08: 0.87.1 against RN 0.86).
# Pin it to the React Native version the project actually has:
RN_VERSION="$(node -p "require('react-native/package.json').version")"
npm install --save-dev --legacy-peer-deps "@react-native/jest-preset@${RN_VERSION}"
```

(Without the `@react-native/jest-preset` line the very first `jest` run dies with *"The React Native Jest preset that jest-expo relies on has moved to a separate package"*; with the wrong minor it dies with *"Could not locate module react-native/setup-env"* — observed 2026-09-08 on SDK 57 / RN 0.86.3. On SDK 58 the suite also needs `"resolver": "react-native-reanimated/jest/resolver"` in the jest config, or `setUpTests()` crashes every suite — re-run 2026-10-17 on a fresh SDK 58 app. `test-renderer@^1` is a **required** peer dep of RNTL v14 — it replaced `react-test-renderer`. Node `^22.13 || >=24` is also required. Native matchers like `toBeOnTheScreen()` are built into `@testing-library/react-native` v12.4+ — no separate `@testing-library/jest-native` needed. See `references/jest-setup.md` for the full config.)

⚠️ **RNTL v14 is async**: `render`, `renderHook`, `fireEvent` and `act` all return Promises and MUST be awaited, and the `UNSAFE_*` queries are gone. Read the v14 section of `references/rntl-patterns.md` before writing a test — a missing `await` produces a test that passes for the wrong reason. Migrating an existing suite: `npx codemod@latest rntl-v14-update-deps --target .` then `npx codemod@latest rntl-v14-async-functions --target ./src`.

For Maestro: detect `.maestro/` directory. If missing AND user wants e2e, see `references/maestro.md` (manual install, not npm — Java 17+, `.maestro/` flow layout, `testID` conventions, and ⚠️ the Expo Go vs dev-build caveat: Expo Go can't `launchApp` your own `appId`, use `openLink: exp://…`).

### Step 3 — Choose what to test

Ask the user (one round-trip) what to test:
- **Component** (renders, props, interactions) → RNTL.
- **Hook** (`useQuery`, `useMutation`, custom hook) → RNTL `await renderHook(...)`.
- **Pure function** (utility, helper) → plain Jest.
- **e2e flow** (sign-in, navigation, form submit) → Maestro.
- **Shared-state / two-user flow** (user A takes a seat, a slot, an invite; user B must see it taken) → Maestro, two `runFlow` sign-ins in one file with `clearState` between them, against a real test backend — `references/maestro.md` §Two-user flows. Jest cannot express this; it is the check that finds authorization bugs between identities.

**On a project whose `stack.test` carries the `e2e` layer**, the agentic runner (`@e2e-dev/mobile`,
built on agent-device) is the other way to write an e2e flow — a native accessibility tree the agent
drives, on simulators, emulators and connected phones. There is **one copy of that text**, in
`write-tests/references/e2e-agentic.md`: one technique, one authority, whichever platform asks.
Read it before writing one, for the cost rule and for the gates. ⚠️ **It does not take the two-user
flow from Maestro**: e2e on mobile has no session save/restore, so that check stays where it is.

If the file under test is `lib/api.ts`, write a Jest test of the function with `fetch` mocked.
If the file under test is `app/(auth)/sign-in.tsx`, write an RNTL test of the screen + a Maestro flow of the user journey.

### Step 4 — Write the test

Use the canonical patterns from `references/rntl-patterns.md` (or `references/jest-setup.md` for utilities, or `references/maestro.md` for e2e).

Test files:
- Unit/integration: `__tests__/<mirror-source-path>.test.tsx` (or `.test.ts` for non-JSX).
- e2e: `.maestro/<flow-name>.yaml`.

### Step 5 — Run the test

```bash
cd <project-root> && npm test -- --runInBand --bail
```

(For Maestro: `maestro test .maestro/<flow-name>.yaml`.)

Test MUST pass. If it doesn't:
1. Read the failure. Is it a missing mock? An assumption about the test API?
2. Fix the test (NOT the source — that's a separate task).
3. Re-run.

### Step 6 — Commit

```bash
git add __tests__/<path>.test.tsx
git commit -m "test(<area>): cover <what>"
```

## Common anti-patterns (NEVER do)

- ❌ **Detox** — heavy, Expo Go unfriendly, requires native build. Use Maestro.
- ❌ **Enzyme** — abandoned. RNTL only.
- ❌ Snapshot for every component — only for stable design-system primitives.
- ❌ `jest.fn()` without typing the return → tests pass on wrong shape.
- ❌ Testing implementation details (state names, internal function calls) → brittle. Test user-facing behavior.
- ❌ Testing TanStack Query result shape directly → mock the queryFn instead and assert UI.
- ❌ Skipping cleanup → tests leak state. Always use RNTL's auto-cleanup (default).
- ❌ Sleeping `await new Promise(r => setTimeout(r, 1000))` → use `waitFor` with explicit assertions.

## Updating meta.json (recommended pattern)

This skill does NOT advance `meta.json#phase` (see Contract above) — never call `set-phase`. It only records the artifact and appends history, using the canonical script when available:

```bash
# Wherever dev-flow is installed (e.g. ~/.claude/skills/dev-flow/), invoke:
python3 .../dev-flow/scripts/update_meta.py <project-root> record-artifact \
    --path <relative-path> --produced-by '<this-skill-name>' [--derived-from <p1> <p2> ...]
python3 .../dev-flow/scripts/update_meta.py <project-root> append-history \
    --skill '<this-skill-name>' --inputs '{...}' --outputs '{...}' --phase-after <current_phase>
```

The script normalizes legacy kebab-case aliases (e.g. `module-added` → `module_added`) and writes the canonical sha256 + timestamp into `meta.json#artifacts`. **Fall back to direct JSON editing only if the script is not on PATH** (and warn the user).

## Sources

- Course: codewithbeto.dev/rnCourse — "Testing" module (paid, distilled).
- Official: https://callstack.github.io/react-native-testing-library/
- Official: https://docs.expo.dev/develop/unit-testing/
- Official: https://maestro.mobile.dev/

Files in this skill

  • SKILL.md8.1 KB
  • references/contracts.md80.5 KB
  • references/decision-tree.md3.7 KB
  • references/jest-setup.md7.5 KB
  • references/maestro.md15.7 KB
  • references/rntl-patterns.md8.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.