ReadyNative

Jest + Testing Library

Set up Jest + Testing Library for testing in an Expo app with ReadyNative: jest-expo preset, RNTL, tests for stores/theme/ui

Expo Go: yes

Runs in Expo Go; no dev build needed for this module.

Jest on jest-expo, with React Native Testing Library and the mocks a real Expo app needs. I'd keep this on - it's what makes swapping modules safe, since each module ships its own tests and they disappear with it. Pick testing/none only if you're bringing another runner. Tests import from @jest/globals and render screens wrapped in the kit's ThemeProvider, so they're stack-agnostic.

Setup

bun run setup --testing jest

That's it - setup adds jest.setup.ts (in-memory expo-sqlite/kv-store, a virtual expo-localization, plus reanimated, haptics and symbols mocks), the core tests under src/**/__tests__/ (stores, theme, env, the i18n shim, the kit's Button, List, Row and Toast, the Settings screen), the dev dependencies (jest, jest-expo, @testing-library/react-native, @jest/globals, react-test-renderer, types) and the test script.

Usage

bun run test           # everything
npx jest src/stores    # one folder
npx jest --watch       # while you work

A test looks like this. React Native Testing Library 14's render returns a promise, so await it:

import { expect, it } from "@jest/globals";
import { render, screen } from "@testing-library/react-native";

import { ThemeProvider } from "@/components/ui";
import { SettingsScreen } from "@/screens/settings/settings-screen";

it("renders the title", async () => {
  await render(
    <ThemeProvider>
      <SettingsScreen />
    </ThemeProvider>
  );
  expect(screen.getByText("Settings")).toBeTruthy();
});

That's the finalized tree; before setup, or with --keep-modules, import ThemeProvider from @/ui. Wrap in whatever else the screen reads - a screen that runs a query needs your query provider too.

Module-specific tests (client.test.ts, field.test.tsx, i18n.test.tsx, and example-store.test.ts with --with-examples) are owned by their modules, so they arrive and leave with the module.

Gotchas

  • jest.config.js is a core file, not part of this module, because the setup engine shares it. That means testing/none leaves it in place - harmless, unused.
  • Each UI stack adds its own jest setup where it needs one (src/components/ui/jest-setup.ts after setup), required by jest.setup.ts. Unistyles' official mock strips variants; Tamagui needs its ESM entries in transformIgnorePatterns (already handled).
  • Picking --testing none at setup (or later in a --keep-modules tree) removes the setup file, every test file and the dev dependencies. In a finalized tree, delete them by hand.

Remove it

While modules/ exists (a tree set up with --keep-modules), setup does all of it:

bun run setup --testing none --yes --keep-modules

In a finalized tree setup is a stub, so you undo it by hand. Here is everything this module added:

  1. Delete the files that are still there: jest.setup.ts, src/lib/__tests__/env.test.ts, src/lib/__tests__/i18n-shim.test.ts, src/lib/__tests__/analytics-shim.test.ts, src/lib/__tests__/confirm.test.ts, src/lib/weather/__tests__/weather.test.ts, src/lib/__tests__/cache.test.ts, src/lib/__tests__/native-toast.test.ts, src/lib/__tests__/privacy.test.ts, src/screens/__tests__/developer-tools.test.tsx, src/screens/__tests__/settings-screen.test.tsx, src/stores/__tests__/create-store.test.ts, src/stores/__tests__/onboarding.test.ts, src/stores/__tests__/consent.test.ts, src/stores/__tests__/theme-mode.test.ts, src/theme/__tests__/mode.test.ts, src/theme/__tests__/mode.web.test.ts, src/theme/__tests__/tokens.test.ts, src/ui/__tests__/button.test.tsx, src/ui/__tests__/list.test.tsx, src/ui/__tests__/row.test.tsx, src/ui/__tests__/toast.test.tsx, src/lib/__tests__/store-review.test.ts, src/lib/__tests__/clipboard-share-browser.test.ts, src/components/__tests__/updates-banner.test.tsx, jest.config.js, scripts/test-utils/fixture.ts, scripts/__tests__/apply.test.ts, scripts/__tests__/doctor.test.ts, scripts/__tests__/e2e.test.ts, scripts/__tests__/gen-links.test.ts, scripts/__tests__/gen-privacy.test.ts, scripts/__tests__/graph.test.ts, scripts/__tests__/pm.test.ts, scripts/__tests__/store-check.test.ts, scripts/__tests__/gen-theme.test.ts, scripts/__tests__/gen-theme-tamagui.test.ts, scripts/__tests__/gen-theme-nativewind4.test.ts, scripts/__tests__/generate.test.ts, scripts/__tests__/registry.test.ts, scripts/__tests__/resolve.test.ts, scripts/__tests__/scaffold-clean.test.ts, scripts/__tests__/setup-clean.test.ts, scripts/__tests__/finalize.test.ts, scripts/__tests__/scaffold-tab.test.ts, scripts/__tests__/__snapshots__/gen-theme.test.ts.snap, scripts/__tests__/__snapshots__/gen-theme-tamagui.test.ts.snap, scripts/__tests__/__snapshots__/gen-theme-nativewind4.test.ts.snap, scripts/__tests__/__snapshots__/generate.test.ts.snap, scripts/__tests__/gen-assets.test.ts, src/components/__tests__/error-boundary.test.tsx, src/hooks/__tests__/use-sign-out-cleanup.test.tsx.
  2. Uninstall the dependencies: bun remove @jest/globals @testing-library/react-native @types/jest @types/react-test-renderer jest jest-expo react-test-renderer.
  3. Delete the script test from package.json.
  4. Check it: bun run typecheck and bun run lint point at anything that still imports the removed files; bun run gen:graph refreshes docs/ARCHITECTURE.md.

Reference

Everything below is generated from modules/testing/jest/module.json - the same file bun run setup reads, so it is what actually lands in your repo.

Install

bun run setup --testing jest

Module id: testing/jest (the default for this category).

Dependencies

PackageVersionKind
@jest/globals~29.7.0devDependency
@testing-library/react-native^14.0.1devDependency
@types/jest29.5.14devDependency
@types/react-test-renderer^19.3.0devDependency
jest~29.7.0devDependency
jest-expo~57.0.5devDependency
react-test-renderer19.2.3devDependency

Files

52 files copied to the project root
  • jest.setup.ts
  • src/lib/__tests__/env.test.ts
  • src/lib/__tests__/i18n-shim.test.ts
  • src/lib/__tests__/analytics-shim.test.ts
  • src/lib/__tests__/confirm.test.ts
  • src/lib/weather/__tests__/weather.test.ts
  • src/lib/__tests__/cache.test.ts
  • src/lib/__tests__/native-toast.test.ts
  • src/lib/__tests__/privacy.test.ts
  • src/screens/__tests__/developer-tools.test.tsx
  • src/screens/__tests__/settings-screen.test.tsx
  • src/stores/__tests__/create-store.test.ts
  • src/stores/__tests__/onboarding.test.ts
  • src/stores/__tests__/consent.test.ts
  • src/stores/__tests__/theme-mode.test.ts
  • src/theme/__tests__/mode.test.ts
  • src/theme/__tests__/mode.web.test.ts
  • src/theme/__tests__/tokens.test.ts
  • src/ui/__tests__/button.test.tsx
  • src/ui/__tests__/list.test.tsx
  • src/ui/__tests__/row.test.tsx
  • src/ui/__tests__/toast.test.tsx
  • src/lib/__tests__/store-review.test.ts
  • src/lib/__tests__/clipboard-share-browser.test.ts
  • src/components/__tests__/updates-banner.test.tsx
  • jest.config.js
  • scripts/test-utils/fixture.ts
  • scripts/__tests__/apply.test.ts
  • scripts/__tests__/doctor.test.ts
  • scripts/__tests__/e2e.test.ts
  • scripts/__tests__/gen-links.test.ts
  • scripts/__tests__/gen-privacy.test.ts
  • scripts/__tests__/graph.test.ts
  • scripts/__tests__/pm.test.ts
  • scripts/__tests__/store-check.test.ts
  • scripts/__tests__/gen-theme.test.ts
  • scripts/__tests__/gen-theme-tamagui.test.ts
  • scripts/__tests__/gen-theme-nativewind4.test.ts
  • scripts/__tests__/generate.test.ts
  • scripts/__tests__/registry.test.ts
  • scripts/__tests__/resolve.test.ts
  • scripts/__tests__/scaffold-clean.test.ts
  • scripts/__tests__/setup-clean.test.ts
  • scripts/__tests__/finalize.test.ts
  • scripts/__tests__/scaffold-tab.test.ts
  • scripts/__tests__/__snapshots__/gen-theme.test.ts.snap
  • scripts/__tests__/__snapshots__/gen-theme-tamagui.test.ts.snap
  • scripts/__tests__/__snapshots__/gen-theme-nativewind4.test.ts.snap
  • scripts/__tests__/__snapshots__/generate.test.ts.snap
  • scripts/__tests__/gen-assets.test.ts
  • src/components/__tests__/error-boundary.test.tsx
  • src/hooks/__tests__/use-sign-out-cleanup.test.tsx

On this page

Get ReadyNative