agentsclimarketplace

Rn testing

Skill almasumdev/awesome-react-native-agent-skills/.github/skills/testing_and_automation/rn-testing

Expert guidance on unit and component testing in React Native with Jest and React Native Testing Library, including hook tests, mocks, and snapshot discipline. Use when asked about unit tests, component tests, or mocks.From its SKILL.md

Install
npx -y skills add almasumdev/awesome-react-native-agent-skills --skill rn-testing

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.0 KB, ~1.2k tokens by cl100k_base, as published. Nobody here has run it

React Native Testing with Jest + RNTL

Instructions

Use jest with jest-preset-react-native (or jest-expo) and @testing-library/react-native. Test behavior, not implementation.

1. Jest Config

jest.config.js:

module.exports = {
  preset: 'react-native',
  setupFiles: ['./jest.setup.ts'],
  setupFilesAfterEach: ['@testing-library/react-native/extend-expect'],
  transformIgnorePatterns: [
    'node_modules/(?!(?:react-native|@react-native|@react-navigation|expo|expo-.*|@shopify/flash-list|react-native-reanimated)/)',
  ],
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
  testMatch: ['**/*.(spec|test).(ts|tsx)'],
};

jest.setup.ts:

import 'react-native-gesture-handler/jestSetup';

jest.mock('react-native-reanimated', () => require('react-native-reanimated/mock'));

jest.mock('react-native-mmkv', () => {
  const store = new Map<string, string>();
  return {
    MMKV: class {
      getString = (k: string) => store.get(k);
      set = (k: string, v: string) => void store.set(k, v);
      delete = (k: string) => void store.delete(k);
    },
  };
});

2. Component Tests -- Query by User-Visible Attributes

// Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react-native';
import { Button } from '@shared/ui/Button';

describe('Button', () => {
  it('invokes onPress when enabled', () => {
    const onPress = jest.fn();
    render(<Button label="Save" onPress={onPress} />);
    fireEvent.press(screen.getByRole('button', { name: 'Save' }));
    expect(onPress).toHaveBeenCalledTimes(1);
  });

  it('is not pressable when disabled', () => {
    const onPress = jest.fn();
    render(<Button label="Save" onPress={onPress} disabled />);
    const btn = screen.getByRole('button', { name: 'Save' });
    expect(btn).toBeDisabled();
    fireEvent.press(btn);
    expect(onPress).not.toHaveBeenCalled();
  });
});

Prefer role/name queries. Avoid testID except for E2E selectors.

3. Testing Hooks

// useCounter.test.ts
import { renderHook, act } from '@testing-library/react-native';
import { useCounter } from './useCounter';

it('increments', () => {
  const { result } = renderHook(() => useCounter(0));
  act(() => result.current.inc());
  expect(result.current.count).toBe(1);
});

4. Testing with TanStack Query

// useArticles.test.tsx
import { renderHook, waitFor } from '@testing-library/react-native';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useArticles } from './useArticles';

function createWrapper() {
  const qc = new QueryClient({ defaultOptions: { queries: { retry: false, gcTime: 0 } } });
  return function Wrapper({ children }: { children: React.ReactNode }) {
    return <QueryClientProvider client={qc}>{children}</QueryClientProvider>;
  };
}

it('loads articles', async () => {
  globalThis.fetch = jest.fn(async () =>
    new Response(JSON.stringify([{ id: '1', title: 't', body: 'b', updatedAt: 1 }]), { status: 200 }),
  ) as unknown as typeof fetch;

  const { result } = renderHook(() => useArticles(), { wrapper: createWrapper() });
  await waitFor(() => expect(result.current.isSuccess).toBe(true));
  expect(result.current.data).toHaveLength(1);
});

5. Mocking Repositories

Prefer real in-memory fakes over jest.fn() chains:

// src/infrastructure/articleRepository.fake.ts
import type { ArticleRepository } from '@domain/articles/ArticleRepository';
import type { Article } from '@domain/articles/Article';

export function createFakeArticleRepo(seed: Article[] = []): ArticleRepository {
  const store = new Map<string, Article>(seed.map((a) => [a.id, a]));
  return {
    list: async () => [...store.values()],
    get: async (id) => store.get(id) ?? null,
    save: async (a) => void store.set(a.id, a),
  };
}

Injected fakes read as production code, not test-shaped mocks.

6. Snapshot Discipline

  • Use snapshots for stable structural output only (theme tokens, generated config).
  • Never snapshot screens -- they change often and the diffs are unreadable.
  • Always review diffs manually before accepting an updated snapshot.

7. CI

  • Run with --ci --runInBand --coverage.
  • Fail if coverage drops under a project-wide threshold in jest.config.js:
coverageThreshold: {
  global: { branches: 70, functions: 75, lines: 80, statements: 80 },
}

Checklist

  • Components are tested via role / label queries, not testID or DOM internals.
  • Hooks use renderHook + act for updates.
  • TanStack Query tests use a per-test QueryClient with retry: false.
  • Reanimated and MMKV are mocked once in jest.setup.ts.
  • Repository dependencies have in-memory fakes, not ad-hoc jest.fn() chains.
  • Snapshots are used sparingly and reviewed on every update.
  • CI enforces a coverage threshold.

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.