---
url: /react-native-testing-library/docs/start/intro.md
---

# Introduction

## The problem

You want to write maintainable tests for your React Native components. As a part of this goal, you want your tests to avoid including implementation details of your components and focus on making your tests give you the confidence they are intended. As part of this, you want your tests to be maintainable in the long run so refactors of your components (changes to implementation but not functionality) don't break your tests and slow you and your team down.

## This solution

The React Native Testing Library (RNTL) is a comprehensive solution for testing React Native components. It provides React Native runtime simulation on top of [Test Renderer](https://github.com/mdjastrzebski/test-renderer), in a way that encourages better testing practices. Its primary guiding principle is:

> The more your tests resemble how your software is used, the more confidence they can give you.

This project is inspired by [React Testing Library](https://github.com/testing-library/react-testing-library). It is tested to work with Jest, but it should work with other test runners as well.

## Example

```jsx
import { render, screen, userEvent } from '@testing-library/react-native';
import { QuestionsBoard } from '../QuestionsBoard';

test('form submits two answers', async () => {
  const questions = ['q1', 'q2'];
  const onSubmit = jest.fn();

  const user = userEvent.setup();
  await render(<QuestionsBoard questions={questions} onSubmit={onSubmit} />);

  const answerInputs = screen.getAllByLabelText('answer input');
  await user.type(answerInputs[0], 'a1');
  await user.type(answerInputs[1], 'a2');
  await user.press(screen.getByRole('button', { name: 'Submit' }));

  expect(onSubmit).toHaveBeenCalledWith({
    1: { q: 'q1', a: 'a1' },
    2: { q: 'q2', a: 'a2' },
  });
});
```

You can find the source of the `QuestionsBoard` component and this example [here](https://github.com/callstack/react-native-testing-library/blob/main/src/__tests__/questionsBoard.test.tsx).



---
url: /react-native-testing-library/docs/start/quick-start.md
---

# Quick Start

## Installation

Open a Terminal in your project's folder and run:


```sh [npm]
npm install -D @testing-library/react-native
```

```sh [yarn]
yarn add -D @testing-library/react-native
```

```sh [pnpm]
pnpm add -D @testing-library/react-native
```

```sh [bun]
bun add -D @testing-library/react-native
```

This library has a peer dependency on [Test Renderer](https://github.com/mdjastrzebski/test-renderer). Make sure to install it:


```sh [npm]
npm install -D test-renderer
```

```sh [yarn]
yarn add -D test-renderer
```

```sh [pnpm]
pnpm add -D test-renderer
```

```sh [bun]
bun add -D test-renderer
```

Test Renderer has better compatibility with React 19 and improved type safety compared to the deprecated [React Test Renderer](https://reactjs.org/docs/test-renderer.html).

## Agent docs in the package

RNTL ships package-specific documentation for coding agents in `node_modules/@testing-library/react-native/docs/`.
Add this snippet to your project's `AGENTS.md` or `CLAUDE.md` file so agents use the docs for the installed package version:

```md
# React Native Testing Library in this project

This project uses `@testing-library/react-native`. Its APIs and testing conventions can differ from your training data.
Before writing or changing RNTL tests, read the relevant guide in
`node_modules/@testing-library/react-native/docs/`, starting with
`node_modules/@testing-library/react-native/docs/guides/llm-guidelines.md`.
Prefer those package docs over stale assumptions, and follow deprecation notices.
```

### Jest matchers

RNTL automatically extends Jest with React Native-specific matchers. The only thing you need to do is to import anything from `@testing-library/react-native` which you already need to do to access the `render` function.

### ESLint plugin

Set up [`eslint-plugin-testing-library`](https://github.com/testing-library/eslint-plugin-testing-library) to avoid common Testing Library mistakes and bad practices.

Install the plugin (assuming you already have `eslint` installed & configured):


```sh [npm]
npm install -D eslint-plugin-testing-library
```

```sh [yarn]
yarn add -D eslint-plugin-testing-library
```

```sh [pnpm]
pnpm add -D eslint-plugin-testing-library
```

```sh [bun]
bun add -D eslint-plugin-testing-library
```

Then, add this to your ESLint config (e.g., `.eslintrc.js`). Extend the `react` plugin:

```js title=.eslintrc.js
module.exports = {
  overrides: [
    {
      // Test files only
      files: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'],
      extends: ['plugin:testing-library/react'],
    },
  ],
};
```



---
url: /react-native-testing-library/docs/start/migration-v14.md
---

# Migration to 14.x

This guide describes the migration to React Native Testing Library version 14 from version 13.x.

## Overview

RNTL v14 drops support for React 18 and adopts React 19's async rendering model. Here's what changed:

- React 19.0.0+ and React Native 0.78+ are now required
- Node.js `^22.13.0 || >=24` is now required
- `render`, `renderHook`, `fireEvent`, and `act` are now async
- Switched from deprecated [React Test Renderer](https://reactjs.org/docs/test-renderer.html) to [Test Renderer](https://github.com/mdjastrzebski/test-renderer)
- Removed deprecated APIs: `update`, `getQueriesForElement`, `UNSAFE_root`, `concurrentRoot` option, `createNodeMock` option
- Reintroduced `container` API, which is now safe to use

:::info React 18 Users

If you need to support React 18, please continue using RNTL v13.x.

:::

## Quick Migration

We provide codemods to automate most of the migration:

**Step 1: Update dependencies**


```sh [npx]
npx codemod@latest rntl-v14-update-deps --target .
npm install
```

```sh [yarn]
yarn dlx codemod@latest rntl-v14-update-deps --target .
yarn install
```

```sh [pnpm]
pnpm dlx codemod@latest rntl-v14-update-deps --target .
pnpm install
```

```sh [bunx]
bunx codemod@latest rntl-v14-update-deps --target .
bun install
```

**Step 2: Update test code to async**


```sh [npx]
npx codemod@latest rntl-v14-async-functions --target ./src
```

```sh [yarn]
yarn dlx codemod@latest rntl-v14-async-functions --target ./src
```

```sh [pnpm]
pnpm dlx codemod@latest rntl-v14-async-functions --target ./src
```

```sh [bunx]
bunx codemod@latest rntl-v14-async-functions --target ./src
```

After running the codemods, review the changes and run your tests.

If your project uses coding agents, add the RNTL package-docs instruction snippet from the [Quick Start](/react-native-testing-library/docs/start/quick-start.md#agent-docs-in-the-package) so agents read the docs that match your installed package version.

## Breaking Changes

### Supported React, React Native, and Node.js versions

**This version requires React 19+, React Native 0.78+, and Node.js `^22.13.0 || >=24`.** If you need to support React 18, please use the latest v13.x version.

| RNTL Version | React Version | React Native Version | Node.js Version    |
| ------------ | ------------- | -------------------- | ------------------ |
| v14.x        | >= 19.0.0     | >= 0.78              | ^22.13.0 \|\| >=24 |
| v13.x        | >= 18.0.0     | >= 0.71              | >= 18              |

### Test Renderer replaces React Test Renderer

In v14, React Native Testing Library uses [Test Renderer](https://github.com/mdjastrzebski/test-renderer) instead of the deprecated [React Test Renderer](https://reactjs.org/docs/test-renderer.html). Test Renderer works with React 19 and has better TypeScript support.

**What changed:**

- The underlying renderer is now Test Renderer instead of React Test Renderer
- This is mostly an internal change; your tests should work without modifications in most cases
- Type definitions now use [`TestInstance`](https://github.com/mdjastrzebski/test-renderer#test-instance) from Test Renderer instead of `ReactTestInstance`
- Test Renderer 1.x is required as a peer dependency. Use the recommended Test Renderer version for your React 19 minor version.

#### Recommended Test Renderer versions

Choose the Test Renderer version that matches your React 19 minor version:

| React version | Recommended Test Renderer version | Notable React features                            |
| ------------- | --------------------------------- | ------------------------------------------------- |
| `19.2`        | `test-renderer@1.2`               | `<Activity />`, `useEffectEvent`                  |
| `19.1`        | `test-renderer@1.1`               | Owner Stack support, updated `useId()` format     |
| `19.0`        | `test-renderer@1.0`               | Actions, `useActionState`, `useOptimistic`, `use` |

Using an older Test Renderer line can prevent RNTL from supporting newer React 19 features in your tests. Using a newer Test Renderer line than your React version can produce peer dependency warnings, or an install error with `npm`.

See the [Test Renderer React 19 compatibility lines](https://github.com/mdjastrzebski/test-renderer#react-19-compatibility-lines) for the latest recommendations.

**Migration:**

#### 1. Update dependencies

Run codemod for updating dependencies:


```sh [npx]
npx codemod@latest rntl-v14-update-deps
npm install
```

```sh [yarn]
yarn dlx codemod@latest rntl-v14-update-deps
yarn install
```

```sh [pnpm]
pnpm dlx codemod@latest rntl-v14-update-deps
pnpm install
```

```sh [bunx]
bunx codemod@latest rntl-v14-update-deps
bun install
```

##### Manual changes

Remove React Test Renderer and its type definitions from your dev dependencies, and add the Test Renderer line that matches your React minor version:


```sh [npm]
npm uninstall react-test-renderer @types/react-test-renderer
npm install -D test-renderer@1.2
```

```sh [yarn]
yarn remove react-test-renderer @types/react-test-renderer
yarn add -D test-renderer@1.2
```

```sh [pnpm]
pnpm remove react-test-renderer @types/react-test-renderer
pnpm add -D test-renderer@1.2
```

```sh [bun]
bun remove react-test-renderer @types/react-test-renderer
bun add -D test-renderer@1.2
```

The commands above use `test-renderer@1.2` for React 19.2. Use `test-renderer@1.1` for React 19.1, or `test-renderer@1.0` for React 19.0.

#### 2. Update type imports (if needed)

If you were directly importing types from React Test Renderer, you may need to update your imports:

```ts
// Before (v13)
import type { ReactTestInstance } from 'react-test-renderer';

// After (v14)
import type { TestInstance } from 'test-renderer';
```

**Note:** Most users won't need to update type imports, as React Native Testing Library now exports the necessary types directly.

See the [Test Renderer documentation](https://github.com/mdjastrzebski/test-renderer) for more.

### Async APIs by Default

With React 18 support dropped, RNTL v14 uses React 19's async rendering model. The following functions are now async by default:

- `render()` → returns `Promise<RenderResult>`
- `rerender()` and `unmount()` → return `Promise<void>`
- `renderHook()` → returns `Promise<RenderHookResult>`
- `fireEvent()` and helpers (`press`, `changeText`, `scroll`) → return `Promise<void>`
- `act()` → always returns `Promise<T>`

:::tip Already using async APIs?

If you adopted the async APIs introduced in RNTL v13.3 (`renderAsync`, `fireEventAsync`, `renderHookAsync`), rename them to their non-async counterparts (`render`, `fireEvent`, `renderHook`). The async versions have been removed since the standard APIs are now async by default.

:::

#### `render` is now async \{#render-async-default}

In v14, `render` is async by default and returns a Promise. This allows proper support for `Suspense` boundaries and the `use()` hook.

**Before (v13):**

```ts
import { render, screen } from '@testing-library/react-native';

it('should render component', () => {
  render(<MyComponent />);
  expect(screen.getByText('Hello')).toBeOnTheScreen();
});
```

**After (v14):**

```ts
import { render, screen } from '@testing-library/react-native';

it('should render component', async () => {
  await render(<MyComponent />);
  expect(screen.getByText('Hello')).toBeOnTheScreen();
});
```

See the [`render` API documentation](/react-native-testing-library/docs/api/render.md).

#### `renderHook` is now async

In v14, `renderHook` is async by default and returns a Promise.

**Before (v13):**

```ts
import { renderHook } from '@testing-library/react-native';

it('should test hook', () => {
  const { result, rerender } = renderHook(() => useMyHook());

  rerender(newProps);
  unmount();
});
```

**After (v14):**

```ts
import { renderHook } from '@testing-library/react-native';

it('should test hook', async () => {
  const { result, rerender } = await renderHook(() => useMyHook());

  await rerender(newProps);
  await unmount();
});
```

See the [`renderHook` API documentation](/react-native-testing-library/docs/api/misc/render-hook.md).

#### `fireEvent` is now async

In v14, `fireEvent` and its helpers (`press`, `changeText`, `scroll`) are async by default and return a Promise.

**Before (v13):**

```ts
import { fireEvent, screen } from '@testing-library/react-native';

it('should press button', () => {
  render(<MyComponent />);
  fireEvent.press(screen.getByText('Press me'));
  expect(onPress).toHaveBeenCalled();
});
```

**After (v14):**

```ts
import { fireEvent, screen } from '@testing-library/react-native';

it('should press button', async () => {
  await render(<MyComponent />);
  await fireEvent.press(screen.getByText('Press me'));
  expect(onPress).toHaveBeenCalled();
});
```

`fireEvent.press()` and `fireEvent.scroll()` now create default synthetic native event objects. If you pass event props, they are deep-merged into the default event object:

```ts
await fireEvent.press(screen.getByText('Press me'), {
  nativeEvent: { pageX: 20, pageY: 30 },
});

expect(onPress).toHaveBeenCalledWith(
  expect.objectContaining({
    nativeEvent: expect.objectContaining({ pageX: 20, pageY: 30 }),
  }),
);
```

If your v13 assertions expected the handler to receive exactly the object you passed to `fireEvent.press()` or `fireEvent.scroll()`, update them to use partial matching.

#### `act` is now async

In v14, `act` is async by default and always returns a Promise. You should always `await` the result of `act()`.

**What changed:**

- `act` now always returns `Promise<T>` instead of `T | Thenable<T>`
- `act` should always be awaited

:::note

The transition to async `act` may prevent testing very short transient states, as awaiting `act` will flush all pending updates before returning.

:::

**Before (v13):**

```ts
import { act } from '@testing-library/react-native';

it('should update state', () => {
  act(() => {
    setState('new value');
  });
  expect(state).toBe('new value');
});
```

**After (v14):**

```ts
import { act } from '@testing-library/react-native';

it('should update state', async () => {
  await act(() => {
    setState('new value');
  });
  expect(state).toBe('new value');
});
```

**Note**: Even if your callback is synchronous, you should still use `await act(...)` as `act` now always returns a Promise.

See the [`act` API documentation](/react-native-testing-library/docs/api/misc/other.md#act).

#### Why async APIs?

The async APIs properly handle `Suspense` boundaries and the `use()` hook, and ensure all pending React updates complete before assertions run. This matches React 19's async rendering model.

### Removed APIs

#### `update` alias removed

The `update` alias for `rerender` has been removed. Use `rerender` instead:

```ts
// Before (v13)
screen.update(<MyComponent />);
const { update } = render(<MyComponent />);
update(<MyComponent newProp />);

// After (v14)
await screen.rerender(<MyComponent />);
const { rerender } = await render(<MyComponent />);
await rerender(<MyComponent newProp />);
```

#### `getQueriesForElement` export removed

The `getQueriesForElement` export alias for `within` has been removed. Use `within` instead:

```ts
// Before (v13)
import { getQueriesForElement } from '@testing-library/react-native';

const queries = getQueriesForElement(element);

// After (v14)
import { within } from '@testing-library/react-native';

const queries = within(element);
```

**Note:** `getQueriesForElement` was just an alias for `within`, so the functionality is identical - only the import needs to change.

#### `UNSAFE_root` removed

`UNSAFE_root` has been removed. Use `container` to access the pseudo-element container, or `root` to access the first rendered host element:

```ts
// Before (v13)
const unsafeRoot = screen.UNSAFE_root;

// After (v14)
const container = screen.container; // pseudo-element container
const root = screen.root; // first rendered host element
```

#### Legacy `UNSAFE_*` queries removed

The legacy `UNSAFE_getAllByType`, `UNSAFE_getByType`, `UNSAFE_getAllByProps`, and `UNSAFE_getByProps` queries have been removed. These queries could return composite (user-defined) components, which is no longer supported with [Test Renderer](https://github.com/mdjastrzebski/test-renderer) as it only renders host elements.

If you were using these legacy queries, you should refactor your tests to use the standard queries (`getByRole`, `getByText`, `getByTestId`, etc.) which target host elements.

```ts
// Before (v13)
const buttons = screen.UNSAFE_getAllByType(Button);
const input = screen.UNSAFE_getByProps({ placeholder: 'Enter text' });

// After (v14)
const buttons = screen.getAllByRole('button');
const input = screen.getByPlaceholderText('Enter text');
```

#### `concurrentRoot` option removed

The `concurrentRoot` option has been removed from both `render` options and `configure` function. In v14, concurrent rendering is always enabled, since it's the standard rendering mode for React 19 and React Native's New Architecture.

```ts
// Before (v13)
render(<MyComponent />, { concurrentRoot: true });  // Enable concurrent mode
render(<MyComponent />, { concurrentRoot: false }); // Disable concurrent mode
configure({ concurrentRoot: false });               // Disable globally

// After (v14)
await render(<MyComponent />); // Always uses concurrent rendering
```

**Migration:** Remove any `concurrentRoot` options from your `render` calls and `configure` function. If you were setting `concurrentRoot: true`, just remove the option. If you were setting `concurrentRoot: false` to disable concurrent rendering, this is no longer supported in v14.

#### `createNodeMock` option removed

The `createNodeMock` render option has been removed. It was previously passed through to React Test Renderer, but the v14 Test Renderer integration does not support this option.

```ts
// Before (v13)
render(<MyComponent />, {
  createNodeMock: (element) => {
    if (element.type === TextInput) {
      return { focus: jest.fn() };
    }
    return {};
  },
});

// After (v14)
await render(<MyComponent />);
```

If you used `createNodeMock` to mock imperative refs, prefer testing user-visible behavior and mock the component or native module at the boundary where the ref behavior is introduced.

### `container` API reintroduced

In v14, the `container` API has been reintroduced and is now safe to use. Previously, `container` was renamed to `UNSAFE_root` in v12 due to behavioral differences from React Testing Library's `container`. Now `container` returns a pseudo-element container whose children are the elements you rendered, consistent with React Testing Library's behavior.

**What changed:**

- `screen.container` is now available and safe to use
- `container` returns a pseudo-element container from Test Renderer
- The container's children are the elements you rendered
- `UNSAFE_root` has been removed

**Before (v13):**

```ts
import { render, screen } from '@testing-library/react-native';

it('should access root', () => {
  render(<MyComponent />);
  // UNSAFE_root was the only way to access the container
  const root = screen.UNSAFE_root;
});
```

**After (v14):**

```ts
import { render, screen } from '@testing-library/react-native';

it('should access container', async () => {
  await render(<MyComponent />);
  // container is now safe and available
  const container = screen.container;
  // root is the first child of container
  const root = screen.root;
});
```

See the [`screen` API documentation](/react-native-testing-library/docs/api/screen.md#container).

### Text string validation enforced by default

In v14, Test Renderer enforces React Native's requirement that text strings must be rendered within a `<Text>` component. The `unstable_validateStringsRenderedWithinText` option has been removed from `RenderOptions` since this validation is now always on.

**What changed:**

- Text string validation is now always enabled and cannot be disabled
- The `unstable_validateStringsRenderedWithinText` option has been removed
- Tests will now throw `Invariant Violation: Text strings must be rendered within a <Text> component` errors when attempting to render strings outside of `<Text>` components, matching React Native's runtime behavior

**Migration:**

If you were using `unstable_validateStringsRenderedWithinText: true` in your render options, you can simply remove this option as the validation is now always enabled:

```ts
// Before (v13)
render(<MyComponent />, {
  unstable_validateStringsRenderedWithinText: true,
});

// After (v14)
await render(<MyComponent />);
// Validation is now always enabled
```

If you were relying on the previous behavior where strings could be rendered outside of `<Text>` components, you'll need to fix your components to wrap strings in `<Text>` components, as this matches React Native's actual runtime behavior.

### Hidden Suspense and Activity content

When React keeps previously rendered content hidden, such as suspended content or React 19.2 `<Activity mode="hidden">`, RNTL now applies React Native-like hidden props to the affected instances. In practice, hidden instances receive `display: 'none'`, preserving existing styles by appending the hidden style.

This makes visibility queries and matchers behave closer to runtime behavior:

- default queries do not match hidden instances
- queries with `{ includeHiddenElements: true }` can still find hidden instances
- `toBeVisible()` treats these instances as hidden

### Accessible name changes

Accessible name calculation has been updated to better match React Native behavior:

- `aria-labelledby` and `accessibilityLabelledBy` still take precedence over explicit labels
- `aria-label` and `accessibilityLabel` take precedence over text content
- `Image` `alt` is used as an accessible label
- a root `TextInput` can use its `placeholder` as its accessible name
- child accessible names are concatenated with spaces
- child `TextInput` placeholders are not used when computing a parent's accessible name

This affects `getByRole(..., { name })` and `toHaveAccessibleName()`. If a v13 role query matched because descendant text or label queries were used as a fallback, update the test to match the element's computed accessible name directly or query the descendant element instead.

### Unknown options now warn

RNTL now logs a warning when unknown options are passed to `configure`, `render`, `renderHook`, or `userEvent.setup`. This helps catch stale v13 options and misspellings during migration.

For example, after removing `concurrentRoot`, `createNodeMock`, or `unstable_validateStringsRenderedWithinText`, make sure those options are not still being passed through shared test helpers.

## Codemods

Two codemods are available to automate the migration. Both are safe to run multiple times - they only transform code that hasn't been migrated yet.

### `rntl-v14-update-deps`

Updates your `package.json`:

- Removes React Test Renderer (`react-test-renderer` and `@types/react-test-renderer`)
- Adds Test Renderer (`test-renderer`) using the current recommended 1.x line
- Updates `@testing-library/react-native` to the v14 version


```sh [npx]
npx codemod@latest rntl-v14-update-deps --target .
npm install
```

```sh [yarn]
yarn dlx codemod@latest rntl-v14-update-deps --target .
yarn install
```

```sh [pnpm]
pnpm dlx codemod@latest rntl-v14-update-deps --target .
pnpm install
```

```sh [bunx]
bunx codemod@latest rntl-v14-update-deps --target .
bun install
```

### `rntl-v14-async-functions`

Transforms test files:

- Adds `await` to `render()`, `act()`, `renderHook()`, `fireEvent()` calls
- Makes test functions async when needed
- Handles `screen.rerender()`, `screen.unmount()`, and renderer methods


```sh [npx]
npx codemod@latest rntl-v14-async-functions --target ./src
```

```sh [yarn]
yarn dlx codemod@latest rntl-v14-async-functions --target ./src
```

```sh [pnpm]
pnpm dlx codemod@latest rntl-v14-async-functions --target ./src
```

```sh [bunx]
bunx codemod@latest rntl-v14-async-functions --target ./src
```

#### Custom render functions

If you have custom render helpers (like `renderWithProviders`), you can specify them using the `customRenderFunctions` parameter. The codemod will then also transform calls to these functions:


```sh [npx]
npx codemod@latest rntl-v14-async-functions \
  --target ./src \
  --param customRenderFunctions="renderWithProviders,renderWithTheme"
```

```sh [yarn]
yarn dlx codemod@latest rntl-v14-async-functions \
  --target ./src \
  --param customRenderFunctions="renderWithProviders,renderWithTheme"
```

```sh [pnpm]
pnpm dlx codemod@latest rntl-v14-async-functions \
  --target ./src \
  --param customRenderFunctions="renderWithProviders,renderWithTheme"
```

```sh [bunx]
bunx codemod@latest rntl-v14-async-functions \
  --target ./src \
  --param customRenderFunctions="renderWithProviders,renderWithTheme"
```

This will add `await` to your custom render calls and make the containing test functions async, just like it does for the standard `render` function.

#### Limitations

- Helper functions defined in test files are not transformed by default
- Namespace imports (`import * as RNTL`) are not handled

## Full Changelog

[https://github.com/callstack/react-native-testing-library/compare/v13.3.3...v14.0.0](https://github.com/callstack/react-native-testing-library/compare/v13.3.3...v14.0.0)



---
url: /react-native-testing-library/docs/api/render.md
---

# `render` API

## `render` function \{#render}

```ts
async function render<T>(
  element: React.ReactElement<T>,
  options?: RenderOptions,
): Promise<RenderResult>;
```

The `render` function is the entry point for writing React Native Testing Library tests. It deeply renders the given React element and returns helpers to query the output. The function is async and uses async `act` internally, so all pending React updates run before it resolves. This works with async React features like `Suspense` boundaries and the `use()` hook.

```jsx
import { render, screen } from '@testing-library/react-native';

test('basic test', async () => {
  await render(<MyApp />);
  expect(screen.getAllByRole('button', { name: 'start' })).toBeOnTheScreen();
});
```

> When using React context providers like Redux Provider, you'll likely want to wrap the rendered component with them. In such cases, create your own custom `render` method. [Follow this guide on how to set it up](https://testing-library.com/docs/react-testing-library/setup#custom-render).

### Options

You can customize the `render` method by passing options as the second argument:

#### `wrapper`

```ts
wrapper?: React.ComponentType<any>,
```

Wraps the tested component in an additional wrapper component. Use this to create custom render functions for common React Context providers.

:::note Text string validation

Test Renderer enforces React Native's requirement that text strings must be rendered within a `<Text>` component. If you render a `string` value under components other than `<Text>` (e.g., under `<View>`), it throws an `Invariant Violation: Text strings must be rendered within a <Text> component` error. This matches React Native's runtime behavior.

This validation is always enabled and cannot be disabled. Your tests will catch the same text rendering errors that would occur in production.

:::

### Result

The `render` function returns a promise that resolves to the same queries and utilities as the [`screen`](/react-native-testing-library/docs/api/screen.md) object. Use `screen` for queries and the lifecycle methods from the render result when needed.

See [this article](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#not-using-screen) from Kent C. Dodds for more details.

:::note Type information

Query results and element references use the `TestInstance` type from [Test Renderer](https://github.com/mdjastrzebski/test-renderer). If you need to type element variables, import this type directly from `test-renderer`.

:::



---
url: /react-native-testing-library/docs/api/screen.md
---

# `screen` object

```ts
let screen: {
  ...queries;
  rerender(element: React.Element<unknown>): Promise<void>;
  unmount(): Promise<void>;
  debug(options?: DebugOptions): void
  toJSON(): JsonElement | null;
  container: TestInstance;
  root: TestInstance | null;
};
```

The `screen` object provides access to queries and utilities for the currently rendered UI.

This object is assigned after the `render` call and cleared after each test by calling [`cleanup`](/react-native-testing-library/docs/api/misc/other.md#cleanup). If no `render` call has been made in a given test, then it holds a special object and throws a helpful error on each property and method access.

### `...queries`

The main feature of `screen` is its queries for finding elements in the view hierarchy.

See [Queries](/react-native-testing-library/docs/api/queries.md) for a complete list.

#### Example

```jsx
import { render, screen } from '@testing-library/react-native';

test('example', async () => {
  await render(<MyComponent />);
  const buttonStart = screen.getByRole('button', { name: 'start' });
});
```

### `rerender`

```ts
function rerender(element: React.Element<unknown>): Promise<void>;
```

Re-render the in-memory tree with a new root element. This simulates a React update render at the root. If the new element has the same type (and `key`) as the previous element, the tree will be updated; otherwise, it will re-mount a new tree, in both cases triggering the appropriate lifecycle events.

This method is async and uses async `act` internally to execute all pending React updates during updating. This works with async React features like `Suspense` boundaries and the `use()` hook.

```jsx
import { render, screen } from '@testing-library/react-native';

test('async rerender test', async () => {
  await render(<MyComponent initialData="first" />);

  await screen.rerender(<MyComponent initialData="updated" />);
  expect(screen.getByText('updated')).toBeOnTheScreen();
});
```

### `unmount`

```ts
function unmount(): Promise<void>;
```

Unmount the in-memory tree, triggering the appropriate lifecycle events.

This method is async and uses async `act` internally to execute all pending React updates during unmounting. This works with async React features like `Suspense` boundaries and the `use()` hook.

:::note

Usually you should not need to call `unmount` as it is done automatically if your test runner supports `afterEach` hook (like Jest, mocha, Jasmine).

:::

### `debug`

```ts
function debug(options?: { message?: string; mapProps?: MapPropsFunction }): void;
```

Pretty prints deeply rendered component passed to `render`.

#### `message` option \{#debug-message-option}

You can provide a message that will be printed on top.

```jsx
await render(<Component />);
screen.debug({ message: 'optional message' });
```

logs optional message and colored JSX:

```jsx
optional message

<View
  onPress={[Function bound fn]}
>
  <Text>Press me</Text>
</View>
```

#### `mapProps` option \{#debug-map-props-option}

```ts
function debug({ mapProps: (props) => ({}) });
```

You can use the `mapProps` option to transform the props that will be printed :

```jsx
await render(<View style={{ backgroundColor: 'red' }} />);
screen.debug({ mapProps: ({ style, ...props }) => ({ props }) });
```

This will log the rendered JSX without the `style` props.

The `children` prop cannot be filtered out so the following will print all rendered components with all props but `children` filtered out.

This option can be used to target specific props when debugging a query (for instance, keeping only the `children` prop when debugging a `getByText` query).

You can also transform prop values so that they are more readable (e.g., flatten styles).

```ts
import { StyleSheet } from 'react-native';

screen.debug({
  mapProps: ({ style, ...props }) => ({
    style: StyleSheet.flatten(style),
    ...props,
  }),
});
```

Or remove props that have little value when debugging tests, e.g. path prop for svgs

```ts
screen.debug({ mapProps: ({ path, ...props }) => ({ ...props }) });
```

### `toJSON`

```ts
function toJSON(): JsonElement | null;
```

Get the rendered component JSON representation, e.g. for snapshot testing.

### `container`

```ts
const container: TestInstance;
```

Returns a pseudo-element container whose children are the elements you asked to render. This is the root container element from [Test Renderer](https://github.com/mdjastrzebski/test-renderer).

The `container` provides access to the entire rendered tree. Use it to query or manipulate the rendered output, similar to how `container` works in [React Testing Library](https://testing-library.com/docs/react-testing-library/other#container-1).

```jsx
import { render, screen } from '@testing-library/react-native';

test('example', async () => {
  await render(<MyComponent />);
  // container contains the entire rendered tree
  const container = screen.container;
  expect(container).toBeTruthy();
});
```

### `root`

```ts
const root: TestInstance | null;
```

Returns the rendered root [host element](/react-native-testing-library/docs/advanced/testing-env.md#host-and-composite-components), or `null` if nothing was rendered. This is the first child of the `container`, which represents the actual root element you rendered.

This API is useful for component tests where you need to access the root host view without using `*ByTestId` queries or similar methods.

:::note

In rare cases where your root element is a `React.Fragment` with multiple children, the `container` will have more than one child, and `root` will return only the first one. In such cases, use `container.children` to access all rendered elements.

:::

```jsx
import { render, screen } from '@testing-library/react-native';

test('example', async () => {
  await render(
    <View testID="root-view">
      <Text>Hello</Text>
    </View>,
  );
  // root is the View element you rendered
  expect(screen.root.props.testID).toBe('root-view');
});
```



---
url: /react-native-testing-library/docs/api/queries.md
---

# Queries

Queries are one of the main building blocks for the React Native Testing Library. They enable you to find relevant elements in the element tree, which represents your application's user interface when running under tests.

## Accessing queries

All queries described below are accessible in two main ways: through the `screen` object or by capturing the `render` function call result.

### Using `screen` object

```tsx
import { render, screen } from '@testing-library/react-native';

test('accessing queries using "screen" object', async () => {
  await render(...);

  screen.getByRole("button", { name: "Start" });
})
```

Use the `screen` object exported by `@testing-library/react-native` to access queries. This object contains all available query methods bound to the most recently rendered UI.

### Using `render` result

```tsx
import { render } from '@testing-library/react-native';

test('accessing queries using "render" result', async () => {
  const { getByRole } = await render(...);
  getByRole("button", { name: "Start" });
})
```

You can also capture query functions from the `render` function return value. This provides the same query functions as the `screen` object.

## Query parts

Each query is composed of two parts: variant and predicate, which are separated by the `by` word in the middle of the name.

Consider the following query:

```
getByRole()
```

For this query, `getBy*` is the query variant, and `*ByRole` is the predicate.

## Query variant

The query variants describe the expected number (and timing) of matching elements, so they differ in their return type.

| Variant                                                                         | Assertion                     | Return type                       | Is Async? |
| ------------------------------------------------------------------------------- | ----------------------------- | --------------------------------- | --------- |
| [`getBy*`](/react-native-testing-library/docs/api/queries.md#get-by)            | Exactly one matching element  | `TestInstance`                    | No        |
| [`getAllBy*`](/react-native-testing-library/docs/api/queries.md#get-all-by)     | At least one matching element | `Array<TestInstance>`             | No        |
| [`queryBy*`](/react-native-testing-library/docs/api/queries.md#query-by)        | Zero or one matching element  | <code>TestInstance \| null</code> | No        |
| [`queryAllBy*`](/react-native-testing-library/docs/api/queries.md#query-all-by) | No assertion                  | `Array<TestInstance>`             | No        |
| [`findBy*`](/react-native-testing-library/docs/api/queries.md#find-by)          | Exactly one matching element  | `Promise<TestInstance>`           | Yes       |
| [`findAllBy*`](/react-native-testing-library/docs/api/queries.md#find-all-by)   | At least one matching element | `Promise<Array<TestInstance>>`    | Yes       |

Queries work as implicit assertions on the number of matching elements and will throw an error when the assertion fails.

### `getBy*` queries \{#get-by}

```ts
getByX(...): TestInstance
```

`getBy*` queries return the single matching element for a query, and throw an error if no elements match or if more than one match is found. If you need to find more than one element, then use `getAllBy`.

### `getAllBy*` queries \{#get-all-by}

```ts
getAllByX(...): TestInstance[]
```

`getAllBy*` queries return an array of all matching elements for a query and throw an error if no elements match.

### `queryBy*` queries \{#query-by}

```ts
queryByX(...): TestInstance | null
```

`queryBy*` queries return the first matching node for a query, or `null` if no elements match. Use these to assert that an element is not present. They throw if more than one match is found (use `queryAllBy` instead).

### `queryAllBy*` queries \{#query-all-by}

```ts
queryAllByX(...): TestInstance[]
```

`queryAllBy*` queries return an array of all matching nodes for a query and return an empty array (`[]`) when no elements match.

### `findBy*` queries \{#find-by}

```ts
findByX(
  ...,
  waitForOptions?: {
    timeout?: number,
    interval?: number,
  },
): Promise<TestInstance>
```

`findBy*` queries return a promise which resolves when a matching element is found. The promise is rejected if no elements match or if more than one match is found after a default timeout of 1000 ms. If you need to find more than one element use `findAllBy*` queries.

### `findAllBy*` queries \{#find-all-by}

```ts
findAllByX(
  ...,
  waitForOptions?: {
    timeout?: number,
    interval?: number,
  },
): Promise<TestInstance[]>
```

`findAllBy*` queries return a promise which resolves to an array of matching elements. The promise is rejected if no elements match after a default timeout of 1000 ms.

:::info
`findBy*` and `findAllBy*` queries accept optional `waitForOptions` object arguments, which can contain `timeout`, `interval` and `onTimeout` properties which have the same meaning as respective options for [`waitFor`](/react-native-testing-library/docs/api/misc/async.md#waitfor) function.
:::

:::info
In cases when your `findBy*` and `findAllBy*` queries throw when unable to find matching elements, it is helpful to pass `onTimeout: () => { screen.debug(); }` callback using the `waitForOptions` parameter.
:::

## Query predicates

_Note: most methods like this one return a [`TestInstance`](https://github.com/mdjastrzebski/test-renderer#test-instance) with following properties that you may be interested in:_

```typescript
type TestInstance = {
  type: string;
  props: { [propName: string]: any };
  parent: TestInstance | null;
  children: Array<TestInstance | string>;
};
```

### `*ByRole` \{#by-role}

> getByRole, getAllByRole, queryByRole, queryAllByRole, findByRole, findAllByRole

```ts
getByRole(
  role: TextMatch,
  options?: {
    name?: TextMatch
    disabled?: boolean,
    selected?: boolean,
    checked?: boolean | 'mixed',
    busy?: boolean,
    expanded?: boolean,
    value: {
      min?: number;
      max?: number;
      now?: number;
      text?: TextMatch;
    },
    includeHiddenElements?: boolean;
  }
): TestInstance;
```

Returns a `TestInstance` with matching `role` or `accessibilityRole` prop.

:::info
In order for `*ByRole` queries to match an element it needs to be considered an accessibility element:

1. `Text`, `TextInput` and `Switch` elements are these by default.
2. `View` elements need an explicit [`accessible`](https://reactnative.dev/docs/accessibility#accessible) prop set to `true`
3. Some React Native composite components like `Pressable` & `TouchableOpacity` render host `View` element with `accessible` prop already set.

:::

```jsx
import { render, screen } from '@testing-library/react-native';

await render(
  <Pressable accessibilityRole="button" disabled>
    <Text>Hello</Text>
  </Pressable>,
);
const element = screen.getByRole('button');
const element2 = screen.getByRole('button', { name: 'Hello' });
const element3 = screen.getByRole('button', { name: 'Hello', disabled: true });
```

#### Options \{#by-role-options}

- `name`: Finds an element with given `role`/`accessibilityRole` and a matching [accessible name](/react-native-testing-library/docs/api/misc/accessibility.md#accessible-name). The accessible name is the text a screen reader would announce for the element, derived from its label props or text content. See [Accessible name](/react-native-testing-library/docs/api/misc/accessibility.md#accessible-name) for the details.

- `disabled`: You can filter elements by their disabled state (coming either from `aria-disabled` prop or `accessbilityState.disabled` prop). The possible values are `true` or `false`. Querying `disabled: false` will also match elements with `disabled: undefined` (see the [wiki](https://github.com/callstack/react-native-testing-library/wiki/Accessibility:-State) for more details).
  - See [React Native's accessibilityState](https://reactnative.dev/docs/accessibility#accessibilitystate) docs to learn more about the `disabled` state.
  - This option can alternatively be expressed using the [`toBeEnabled()` / `toBeDisabled()`](/react-native-testing-library/docs/api/jest-matchers.md#tobeenabled) Jest matchers.

- `selected`: You can filter elements by their selected state (coming either from `aria-selected` prop or `accessbilityState.selected` prop). The possible values are `true` or `false`. Querying `selected: false` will also match elements with `selected: undefined` (see the [wiki](https://github.com/callstack/react-native-testing-library/wiki/Accessibility:-State) for more details).
  - See [React Native's accessibilityState](https://reactnative.dev/docs/accessibility#accessibilitystate) docs to learn more about the `selected` state.
  - This option can alternatively be expressed using the [`toBeSelected()`](/react-native-testing-library/docs/api/jest-matchers.md#tobeselected) Jest matcher.

- `checked`: You can filter elements by their checked state (coming either from `aria-checked` prop or `accessbilityState.checked` prop). The possible values are `true`, `false`, or `"mixed"`.
  - See [React Native's accessibilityState](https://reactnative.dev/docs/accessibility#accessibilitystate) docs to learn more about the `checked` state.
  - This option can alternatively be expressed using the [`toBeChecked()` / `toBePartiallyChecked()`](/react-native-testing-library/docs/api/jest-matchers.md#tobechecked) Jest matchers.

- `busy`: You can filter elements by their busy state (coming either from `aria-busy` prop or `accessbilityState.busy` prop). The possible values are `true` or `false`. Querying `busy: false` will also match elements with `busy: undefined` (see the [wiki](https://github.com/callstack/react-native-testing-library/wiki/Accessibility:-State) for more details).
  - See [React Native's accessibilityState](https://reactnative.dev/docs/accessibility#accessibilitystate) docs to learn more about the `busy` state.
  - This option can alternatively be expressed using the [`toBeBusy()`](/react-native-testing-library/docs/api/jest-matchers.md#tobebusy) Jest matcher.

- `expanded`: You can filter elements by their expanded state (coming either from `aria-expanded` prop or `accessbilityState.expanded` prop). The possible values are `true` or `false`.
  - See [React Native's accessibilityState](https://reactnative.dev/docs/accessibility#accessibilitystate) docs to learn more about the `expanded` state.
  - This option can alternatively be expressed using the [`toBeExpanded()` / `toBeCollapsed()`](/react-native-testing-library/docs/api/jest-matchers.md#tobeexpanded) Jest matchers.

- `value`: Filter elements by their accessibility value, based on either `aria-valuemin`, `aria-valuemax`, `aria-valuenow`, `aria-valuetext` or `accessibilityValue` props. Accessiblity value conceptually consists of numeric `min`, `max` and `now` entries, as well as string `text` entry.
  - See React Native [accessibilityValue](https://reactnative.dev/docs/accessibility#accessibilityvalue) docs to learn more about the accessibility value concept.
  - This option can alternatively be expressed using the [`toHaveAccessibilityValue()`](/react-native-testing-library/docs/api/jest-matchers.md#tohaveaccessibilityvalue) Jest matcher.

### `*ByLabelText` \{#by-label-text}

> getByLabelText, getAllByLabelText, queryByLabelText, queryAllByLabelText, findByLabelText, findAllByLabelText

```ts
getByLabelText(
  text: TextMatch,
  options?: {
    exact?: boolean;
    normalizer?: (text: string) => string;
    includeHiddenElements?: boolean;
  },
): TestInstance;
```

Returns a `TestInstance` with matching label:

- either by matching [`aria-label`](https://reactnative.dev/docs/accessibility#aria-label)/[`accessibilityLabel`](https://reactnative.dev/docs/accessibility#accessibilitylabel) prop
- or by matching text content of view referenced by [`aria-labelledby`](https://reactnative.dev/docs/accessibility#aria-labelledby-android)/[`accessibilityLabelledBy`](https://reactnative.dev/docs/accessibility#accessibilitylabelledby-android) prop
- or by matching the [`alt`](https://reactnative.dev/docs/image#alt) prop on `Image` elements

When `accessibilityLabelledBy` references multiple elements with an array, their text content is joined with spaces in the referenced order and matched as a single label. `aria-labelledby` follows React Native's single `nativeID` value behavior.

```jsx
import { render, screen } from '@testing-library/react-native';

await render(<MyComponent />);
const element = screen.getByLabelText('my-label');
```

### `*ByPlaceholderText` \{#by-placeholder-text}

> getByPlaceholderText, getAllByPlaceholderText, queryByPlaceholderText, queryAllByPlaceholderText, findByPlaceholderText, findAllByPlaceholderText

```ts
getByPlaceholderText(
  text: TextMatch,
  options?: {
    exact?: boolean;
    normalizer?: (text: string) => string;
    includeHiddenElements?: boolean;
  }
): TestInstance;
```

Returns a `TestInstance` for a `TextInput` with a matching placeholder – may be a string or regular expression.

```jsx
import { render, screen } from '@testing-library/react-native';

await render(<MyComponent />);
const element = screen.getByPlaceholderText('username');
```

### `*ByDisplayValue` \{#by-display-value}

> getByDisplayValue, getAllByDisplayValue, queryByDisplayValue, queryAllByDisplayValue, findByDisplayValue, findAllByDisplayValue

```ts
getByDisplayValue(
  value: TextMatch,
  options?: {
    exact?: boolean;
    normalizer?: (text: string) => string;
    includeHiddenElements?: boolean;
  },
): TestInstance;
```

Returns a `TestInstance` for a `TextInput` with a matching display value – may be a string or regular expression.

```jsx
import { render, screen } from '@testing-library/react-native';

await render(<MyComponent />);
const element = screen.getByDisplayValue('username');
```

### `*ByText` \{#by-text}

> getByText, getAllByText, queryByText, queryAllByText, findByText, findAllByText

```ts
getByText(
  text: TextMatch,
  options?: {
    exact?: boolean;
    normalizer?: (text: string) => string;
    includeHiddenElements?: boolean;
  }
): TestInstance;
```

Returns a `TestInstance` with matching text – may be a string or regular expression.

This method will join `<Text>` siblings to find matches, similarly to [how React Native handles these components](https://reactnative.dev/docs/text#containers). This will allow for querying for strings that will be visually rendered together, but may be semantically separate React components.

```jsx
import { render, screen } from '@testing-library/react-native';

await render(<MyComponent />);
const element = screen.getByText('banana');
```

### `*ByHintText` \{#by-hint-text}

> getByA11yHint, getAllByA11yHint, queryByA11yHint, queryAllByA11yHint, findByA11yHint, findAllByA11yHint
> getByAccessibilityHint, getAllByAccessibilityHint, queryByAccessibilityHint, queryAllByAccessibilityHint, findByAccessibilityHint, findAllByAccessibilityHint
> getByHintText, getAllByHintText, queryByHintText, queryAllByHintText, findByHintText, findAllByHintText

```ts
getByHintText(
  hint: TextMatch,
  options?: {
    exact?: boolean;
    normalizer?: (text: string) => string;
    includeHiddenElements?: boolean;
  },
): TestInstance;
```

Returns a `TestInstance` with matching `accessibilityHint` prop.

```jsx
import { render, screen } from '@testing-library/react-native';

await render(<MyComponent />);
const element = screen.getByHintText('Plays a song');
```

:::info
Please consult [Apple guidelines on how `accessibilityHint` should be used](https://developer.apple.com/documentation/objectivec/nsobject/1615093-accessibilityhint).
:::

### `*ByTestId` \{#by-test-id}

> getByTestId, getAllByTestId, queryByTestId, queryAllByTestId, findByTestId, findAllByTestId

```ts
getByTestId(
  testId: TextMatch,
  options?: {
    exact?: boolean;
    normalizer?: (text: string) => string;
    includeHiddenElements?: boolean;
  },
): TestInstance;
```

Returns a `TestInstance` with matching `testID` prop. `testID` – may be a string or a regular expression.

```jsx
import { render, screen } from '@testing-library/react-native';

await render(<MyComponent />);
const element = screen.getByTestId('unique-id');
```

:::info
Following [the guiding principles](https://testing-library.com/docs/guiding-principles), use this only when other queries don't work for your use case. `testID` attributes don't resemble how your software is used and should be avoided when possible. They're useful for end-to-end testing on real devices, e.g. with Detox. Learn more from the blog post ["Making your UI tests resilient to change"](https://kentcdodds.com/blog/making-your-ui-tests-resilient-to-change).
:::

### Common options

Usually query first argument can be a **string** or a **regex**. All queries take at least the [`hidden`](#hidden-option) option as an optionnal second argument and some queries accept more options which change string matching behaviour. See [TextMatch](#textmatch) for more info.

#### `includeHiddenElements` option

All queries have the `includeHiddenElements` option which affects whether [elements hidden from accessibility](/react-native-testing-library/docs/api/misc/accessibility.md#ishiddenfromaccessibility) are matched by the query. By default queries will not match hidden elements, because the users of the app would not be able to see such elements.

You can configure the default value with the [`configure` function](/react-native-testing-library/docs/api/misc/config.md#configure).

This option is also available as `hidden` alias for compatibility with [React Testing Library](https://testing-library.com/docs/queries/byrole#hidden).

**Examples**

```tsx
await render(<Text style={{ display: 'none' }}>Hidden from accessibility</Text>);

// Exclude hidden elements
expect(
  screen.queryByText('Hidden from accessibility', {
    includeHiddenElements: false,
  }),
).not.toBeOnTheScreen();

// Include hidden elements
expect(
  screen.getByText('Hidden from accessibility', {
    includeHiddenElements: true,
  }),
).toBeOnTheScreen();
```

## TextMatch type

```ts
type TextMatch = string | RegExp;
```

Most of the query APIs take a `TextMatch` as an argument, which means the argument can be either a _string_ or _regex_.

### Examples

Given the following render:

```jsx
await render(<Text>Hello World</Text>);
```

Will **find a match**:

```js
// Matching a string:
screen.getByText('Hello World'); // full string match
screen.getByText('llo Worl', { exact: false }); // substring match
screen.getByText('hello world', { exact: false }); // ignore case-sensitivity

// Matching a regex:
screen.getByText(/World/); // substring match
screen.getByText(/world/i); // substring match, ignore case
screen.getByText(/^hello world$/i); // full string match, ignore case-sensitivity
screen.getByText(/Hello W?oRlD/i); // advanced regex
```

Will **NOT find a match**

```js
// substring does not match
screen.getByText('llo Worl');
// full string does not match
screen.getByText('Goodbye World');

// case-sensitive regex with different case
screen.getByText(/hello world/);
```

### Options \{#text-match-options}

#### Precision

```typescript
type TextMatchOptions = {
  exact?: boolean;
  normalizer?: (text: string) => string;
};
```

Queries that take a `TextMatch` also accept an object as the second argument that can contain options that affect the precision of string matching:

- `exact`: Defaults to `true`; matches full strings, case-sensitive. When false, matches substrings and is not case-sensitive.
  - `exact` has no effect on regex argument.
  - In most cases using a `regex` instead of a string gives you more control over fuzzy matching and should be preferred over `{ exact: false }`.
- `normalizer`: An optional function which overrides normalization behavior. See [Normalization](#normalization).

`exact` option defaults to `true` but if you want to search for a text slice or make text matching case-insensitive you can override it. That being said we advise you to use regex in more complex scenarios.

#### Normalization

Before running any matching logic against text, it is automatically normalized. By default, normalization consists of trimming whitespace from the start and end of text, and collapsing multiple adjacent whitespace characters into a single space.

If you want to prevent that normalization, or provide alternative normalization (e.g. to remove Unicode control characters), you can provide a `normalizer` function in the options object. This function will be given a string and is expected to return a normalized version of that string.

:::info
Specifying a value for `normalizer` replaces the built-in normalization, but you can call `getDefaultNormalizer` to obtain a built-in normalizer, either to adjust that normalization or to call it from your own normalizer.
:::

`getDefaultNormalizer` take options object which allows the selection of behaviour:

- `trim`: Defaults to `true`. Trims leading and trailing whitespace.
- `collapseWhitespace`: Defaults to `true`. Collapses inner whitespace (newlines, tabs repeated spaces) into a single space.

##### Normalization Examples

To perform a match against text without trimming:

```typescript
screen.getByText('text', {
  normalizer: getDefaultNormalizer({ trim: false }),
});
```

To override normalization to remove some Unicode characters whilst keeping some (but not all) of the built-in normalization behavior:

```typescript
screen.getByText('text', {
  normalizer: (str) => getDefaultNormalizer({ trim: false })(str).replace(/[\u200E-\u200F]*/g, ''),
});
```



---
url: /react-native-testing-library/docs/api/jest-matchers.md
---

# Jest matchers

This guide covers the built-in Jest matchers. These matchers make your tests easier to read and work better with accessibility features.

## Setup

No setup needed. Matchers are available when you import from `@testing-library/react-native`.

## Checking element existence

### `toBeOnTheScreen()`

```ts
expect(element).toBeOnTheScreen();
```

Checks if an element is attached to the element tree. If you have a reference to an element and it gets unmounted during the test, this assertion will fail.

## Element Content

### `toHaveTextContent()`

```ts
expect(element).toHaveTextContent(
  text: string | RegExp,
  options?: {
    exact?: boolean;
    normalizer?: (text: string) => string;
  },
)
```

Checks if an element has the specified text content. Accepts `string` or `RegExp`, with optional [text match options](/react-native-testing-library/docs/api/queries.md#text-match-options) like `exact` and `normalizer`.

### `toContainElement()`

```ts
expect(container).toContainElement(
  instance: TestInstance | null,
)
```

Checks if a container element contains another element.

### `toBeEmptyElement()`

```ts
expect(element).toBeEmptyElement();
```

Checks if an element has no child elements or text content.

## Checking element state

### `toHaveDisplayValue()`

```ts
expect(element).toHaveDisplayValue(
  value: string | RegExp,
  options?: {
    exact?: boolean;
    normalizer?: (text: string) => string;
  },
)
```

Checks if a `TextInput` has the specified display value. Accepts `string` or `RegExp`, with optional [text match options](/react-native-testing-library/docs/api/queries.md#text-match-options) like `exact` and `normalizer`.

### `toHaveAccessibilityValue()`

```ts
expect(element).toHaveAccessibilityValue(
  value: {
    min?: number;
    max?: number;
    now?: number;
    text?: string | RegExp;
  },
)
```

Checks if an element has the specified accessible value.

The matcher reads accessibility values from `aria-valuemin`, `aria-valuemax`, `aria-valuenow`, `aria-valuetext`, and `accessibilityValue` props. It only checks the values you specify, so the element can have other accessibility value entries and still match.

For the `text` entry, you can use a string or `RegExp`.

### `toBeEnabled()` / `toBeDisabled` \{#tobeenabled}

```ts
expect(element).toBeEnabled();
expect(element).toBeDisabled();
```

Checks if an element is enabled or disabled from `aria-disabled` or `accessibilityState.disabled` props. An element is disabled if it or any ancestor is disabled.

:::note
These matchers are opposites. Both are available so you can avoid double negations like `expect(element).not.toBeDisabled()`.
:::

### `toBeSelected()`

```ts
expect(element).toBeSelected();
```

Checks if an element is selected from `aria-selected` or `accessibilityState.selected` props.

### `toBeChecked()` / `toBePartiallyChecked()` \{#tobechecked}

```ts
expect(element).toBeChecked();
expect(element).toBePartiallyChecked();
```

Checks if an element is checked or partially checked from `aria-checked` or `accessibilityState.checked` props.

:::note

- `toBeChecked()` only works on `Switch` host elements and elements with `checkbox`, `radio`, or `switch` role.
- `toBePartiallyChecked()` only works on elements with `checkbox` role.

:::

### `toBeExpanded()` / `toBeCollapsed()` \{#tobeexpanded}

```ts
expect(element).toBeExpanded();
expect(element).toBeCollapsed();
```

Checks if an element is expanded or collapsed from `aria-expanded` or `accessibilityState.expanded` props.

:::note
These matchers are opposites for expandable elements (those with explicit `aria-expanded` or `accessibilityState.expanded` props). For non-expandable elements, neither matcher will pass.
:::

### `toBeBusy()`

```ts
expect(element).toBeBusy();
```

Checks if an element is busy from `aria-busy` or `accessibilityState.busy` props.

## Checking element style

### `toBeVisible()`

```ts
expect(element).toBeVisible();
```

Checks if an element is visible.

An element is invisible if it or any ancestor has `display: none` or `opacity: 0` styles, or if it's hidden from accessibility.

### `toHaveStyle()`

```ts
expect(element).toHaveStyle(
  style: StyleProp<Style>,
)
```

Checks if an element has specific styles.

## Other matchers

### `toHaveAccessibleName()`

```ts
expect(element).toHaveAccessibleName(
  name?: string | RegExp,
  options?: {
    exact?: boolean;
    normalizer?: (text: string) => string;
  },
)
```

Checks if an element has the specified [accessible name](/react-native-testing-library/docs/api/misc/accessibility.md#accessible-name). Accepts `string` or `RegExp`, with optional [text match options](/react-native-testing-library/docs/api/queries.md#text-match-options) like `exact` and `normalizer`.

See [Accessible name](/react-native-testing-library/docs/api/misc/accessibility.md#accessible-name) for how the accessible name is derived from an element's label props and text content.

Without a `name` parameter (or with `undefined`), it only checks whether the element has any accessible name.

### `toHaveProp()`

```ts
expect(element).toHaveProp(
  name: string,
  value?: unknown,
)
```

Checks if an element has a prop. Without a `value` (or with `undefined`), it only checks if the prop exists. With a `value`, it checks if the prop's value matches.

:::note
Use this matcher as a last resort when other matchers don't fit your needs.
:::



---
url: /react-native-testing-library/docs/api/events/fire-event.md
---

# Fire Event API

## `fireEvent` \{#fire-event}

:::note
For common events like `press` or `type`, use the [User Event API](/react-native-testing-library/docs/api/events/user-event.md). It simulates events more realistically by emitting a sequence of events with proper event objects that mimic React Native runtime behavior.

Use Fire Event for cases not supported by User Event and for triggering event handlers on composite components.
:::

```ts
function fireEvent(instance: TestInstance, eventName: string, ...data: unknown[]): Promise<unknown>;
```

The `fireEvent` API triggers event handlers on both host and composite components. It traverses the component tree bottom-up from the passed element to find an enabled event handler named `onXxx` where `xxx` is the event name.

Unlike User Event, this API does not automatically pass event object to event handler, this is responsibility of the user to construct such object.

The base `fireEvent(instance, eventName, ...data)` API can pass multiple custom arguments to the handler. Convenience helpers such as `fireEvent.press` and `fireEvent.scroll` are different: they create a default event object and accept one optional object to merge into it.

This function uses async `act` internally to execute all pending React updates during event handling.

```jsx
import { render, screen, fireEvent } from '@testing-library/react-native';

test('fire changeText event', async () => {
  const onEventMock = jest.fn();
  await render(
    // MyComponent renders TextInput which has a placeholder 'Enter details'
    // and with `onChangeText` bound to handleChangeText
    <MyComponent handleChangeText={onEventMock} />,
  );

  await fireEvent(screen.getByPlaceholderText('change'), 'onChangeText', 'ab');
  expect(onEventMock).toHaveBeenCalledWith('ab');
});
```

:::note
`fireEvent` performs checks that should prevent events firing on disabled elements.
:::

An example using `fireEvent` with native events that aren't already aliased by the `fireEvent` api.

```jsx
import { TextInput, View } from 'react-native';
import { fireEvent, render, screen } from '@testing-library/react-native';

const onBlurMock = jest.fn();

await render(
  <View>
    <TextInput placeholder="my placeholder" onBlur={onBlurMock} />
  </View>,
);

// you can omit the `on` prefix
await fireEvent(screen.getByPlaceholderText('my placeholder'), 'blur');
```

FireEvent exposes convenience methods for common events like: `press`, `changeText`, `scroll`, `layout`.

### `fireEvent.press` \{#press}

:::note
Use the User Event [`press()`](/react-native-testing-library/docs/api/events/user-event.md#press) helper instead. It simulates press interactions more realistically, including pressable support.
:::

```tsx
fireEvent.press: (
  instance: TestInstance,
  eventProps?: Record<string, unknown>,
) => Promise<void>
```

Builds a press event object, merges `eventProps` into it, and invokes the `press` handler on the element or nearest eligible parent.

```jsx
import { View, Text, TouchableOpacity } from 'react-native';
import { render, screen, fireEvent } from '@testing-library/react-native';

const onPressMock = jest.fn();
const eventData = {
  nativeEvent: {
    pageX: 20,
    pageY: 30,
  },
};

await render(
  <View>
    <TouchableOpacity onPress={onPressMock}>
      <Text>Press me</Text>
    </TouchableOpacity>
  </View>,
);

await fireEvent.press(screen.getByText('Press me'), eventData);
expect(onPressMock).toHaveBeenCalledWith(expect.objectContaining(eventData));
```

### `fireEvent.changeText` \{#change-text}

:::note
Use the User Event [`type()`](/react-native-testing-library/docs/api/events/user-event.md#type) helper instead. It simulates text change interactions more realistically, including key-by-key typing, element focus, and other editing events.
:::

```tsx
fireEvent.changeText: (
  instance: TestInstance,
  text: string,
) => Promise<void>
```

Invokes `changeText` event handler on the element or parent element in the tree.

```jsx
import { View, TextInput } from 'react-native';
import { render, screen, fireEvent } from '@testing-library/react-native';

const onChangeTextMock = jest.fn();
const CHANGE_TEXT = 'content';

await render(
  <View>
    <TextInput placeholder="Enter data" onChangeText={onChangeTextMock} />
  </View>,
);

await fireEvent.changeText(screen.getByPlaceholderText('Enter data'), CHANGE_TEXT);
```

### `fireEvent.scroll` \{#scroll}

:::note
Prefer [`user.scrollTo`](/react-native-testing-library/docs/api/events/user-event.md#scrollto) over `fireEvent.scroll` for `ScrollView`, `FlatList`, and `SectionList` components. User Event simulates events more realistically based on React Native runtime behavior.
:::

```tsx
fireEvent.scroll: (
  instance: TestInstance,
  eventProps?: Record<string, unknown>,
) => Promise<void>
```

Builds a scroll event object, merges `eventProps` into it, and invokes the `scroll` handler on the element or nearest eligible parent.

#### On a `ScrollView`

```jsx
import { ScrollView, Text } from 'react-native';
import { render, screen, fireEvent } from '@testing-library/react-native';

const onScrollMock = jest.fn();
const eventData = {
  nativeEvent: {
    contentOffset: {
      y: 200,
    },
  },
};

await render(
  <ScrollView testID="scroll-view" onScroll={onScrollMock}>
    <Text>Content</Text>
  </ScrollView>,
);

await fireEvent.scroll(screen.getByTestId('scroll-view'), eventData);
```

### `fireEvent.layout` \{#layout}

:::note
Available since React Native Testing Library 14.1.0.
:::

```tsx
fireEvent.layout: (
  instance: TestInstance,
  layout?: Partial<{ x: number; y: number; width: number; height: number }>,
) => Promise<void>
```

Builds a layout event carrying the given `layout` rectangle and invokes the `layout` handler on the element or nearest eligible parent. Use it to simulate the layout engine measuring an element, e.g. to test components that adapt to a measured size.

The `layout` values are merged onto a zeroed rectangle (`{ x: 0, y: 0, width: 0, height: 0 }`), so pass only the fields your component reads.

```jsx
import { View } from 'react-native';
import { render, screen, fireEvent } from '@testing-library/react-native';

const onLayoutMock = jest.fn();

await render(<View testID="box" onLayout={onLayoutMock} />);

await fireEvent.layout(screen.getByTestId('box'), { width: 320, height: 80 });
```



---
url: /react-native-testing-library/docs/api/events/user-event.md
---

# User Event interactions

## Comparison with Fire Event API

Fire Event is our original event simulation API. It can invoke **any event handler** declared on **either host or composite elements**. Suppose the element does not have `onEventName` event handler for the passed `eventName` event, or the element is disabled. In that case, Fire Event will traverse up the component tree, looking for an event handler on both host and composite elements along the way. By default, it will **not pass any event data**, but the user might provide it in the last argument.

In contrast, User Event provides realistic event simulation for user interactions like `press` or `type`. Each interaction will trigger a **sequence of events** corresponding to React Native runtime behavior. These events will be invoked **only on host elements**, and **will automatically receive event data** corresponding to each event.

If User Event supports a given interaction, prefer it over the Fire Event counterpart. It makes tests more realistic and reliable. When User Event doesn't support the event or you need to invoke event handlers on composite elements, use Fire Event.

## `setup()`

```ts
userEvent.setup(options?: {
  delay?: number;
  advanceTimers?: (delay: number) => Promise<void> | void;
})
```

Example

```ts
const user = userEvent.setup();
```

Creates a User Event object instance, which can be used to trigger events.

### Options \{#setup-options}

- `delay` controls the default delay between subsequent events, e.g., keystrokes.
- `advanceTimers` is a time advancement utility function that should be used for fake timers. The default setup handles both real timers and Jest fake timers.

## `press()`

```ts
press(
  instance: TestInstance,
): Promise<void>
```

Example

```ts
const user = userEvent.setup();
await user.press(element);
```

Simulates a press on any pressable element, e.g. `Pressable`, `TouchableOpacity`, `Text`, `TextInput`, etc. Unlike `fireEvent.press()`, which only calls the `onPress` prop, this function simulates the entire press interaction by reproducing the event sequence emitted by React Native runtime. It triggers additional events like `pressIn` and `pressOut`.

This event will take a minimum of 130 ms to run due to the internal React Native logic. Consider using fake timers to speed up test execution for tests involving `press` and `longPress` interactions.

## `longPress()`

```ts
longPress(
  instance: TestInstance,
  options?: { duration?: number }
): Promise<void>
```

Example

```ts
const user = userEvent.setup();
await user.longPress(element);
```

Simulates a long press user interaction. In React Native, the `longPress` event is emitted when the press duration exceeds the long press threshold (by default, 500 ms). In other aspects, this action behaves similarly to regular `press` action, e.g., by emitting `pressIn` and `pressOut` events. The press duration is customizable through the options, which is useful when using the `delayLongPress` prop.

This event will, by default, take 500 ms to run. Due to internal React Native logic, it will take at least 130 ms regardless of the duration option passed. Consider using fake timers to speed up test execution for tests involving `press` and `longPress` interactions.

### Options \{#longpress-options}

- `duration` - duration of the press in milliseconds. The default value is 500 ms.

## `type()`

```ts
type(
  instance: TestInstance,
  text: string,
  options?: {
    skipPress?: boolean;
    skipBlur?: boolean;
    submitEditing?: boolean;
  }
): Promise<void>
```

Example

```ts
const user = userEvent.setup();
await user.type(textInput, 'Hello world!');
```

Simulates focusing on a `TextInput` element, typing `text` one character at a time, and leaving the element.

This function supports only host `TextInput` elements. Passing other element types will result in throwing an error.

:::note
This function will add text to the text already present in the text input (as specified by `value` or `defaultValue` props). To replace existing text, use [`clear()`](#clear) helper first.
:::

### Options \{#type-options}

- `skipPress` - if true, `pressIn` and `pressOut` events will not be triggered.
- `skipBlur` - if true, `endEditing` and `blur` events will not be triggered when typing is complete.
- `submitEditing` - if true, `submitEditing` event will be triggered after typing the text.

### Sequence of events \{#type-sequence}

The sequence of events depends on the `multiline` prop and the passed options.

Events will not be emitted if the `editable` prop is set to `false`.

**Entering the element**:

- `pressIn` (optional)
- `focus`
- `pressOut` (optional)

The `pressIn` and `pressOut` events are sent by default but can be skipped by passing the `skipPress: true` option.

**Typing (for each character)**:

- `keyPress`
- `change`
- `changeText`
- `selectionChange`
- `contentSizeChange` (only multiline)

**Leaving the element**:

- `submitEditing` (optional)
- `endEditing`
- `blur`

The `submitEditing` event is skipped by default. It can sent by setting the `submitEditing: true` option.
The `endEditing` and `blur` events can be skipped by passing the `skipBlur: true` option.

## `clear()`

```ts
clear(
  instance: TestInstance,
): Promise<void>
```

Example

```ts
const user = userEvent.setup();
await user.clear(textInput);
```

Simulates clearing the content of a `TextInput` element.

This function supports only host `TextInput` elements. Passing other element types will result in throwing an error.

### Sequence of events \{#clear-sequence}

Events will not be emitted if the `editable` prop is set to `false`.

**Entering the element**:

- `focus`

**Selecting all content**:

- `selectionChange`

**Pressing backspace**:

- `keyPress`
- `change`
- `changeText`
- `selectionChange`

**Leaving the element**:

- `endEditing`
- `blur`

## `paste()`

```ts
paste(
  instance: TestInstance,
  text: string,
): Promise<void>
```

Example

```ts
const user = userEvent.setup();
await user.paste(textInput, 'Text to paste');
```

Simulates pasting text into a `TextInput` element.

This function supports only host `TextInput` elements. Passing other element types will result in throwing an error.

### Sequence of events \{#paste-sequence}

Events will not be emitted if the `editable` prop is set to `false`.

**Entering the element**:

- `focus`

**Selecting all content**:

- `selectionChange`

**Pasting the text**:

- `change`
- `changeText`
- `selectionChange`
- `contentSizeChange` (only multiline)

**Leaving the element**:

- `endEditing`
- `blur`

## `scrollTo()` \{#scroll-to}

```ts
scrollTo(
  instance: TestInstance,
  options: {
    y: number;
    momentumY?: number;
    contentSize?: { width: number; height: number };
    layoutMeasurement?: { width: number; height: number };
  } | {
    x: number;
    momentumX?: number;
    contentSize?: { width: number; height: number };
    layoutMeasurement?: { width: number; height: number };
  }
): Promise<void>
```

Example

```ts
const user = userEvent.setup();
await user.scrollTo(scrollView, { y: 100, momentumY: 200 });
```

Simulates scrolling a host `ScrollView` element.

This function supports only host `ScrollView` elements, passing other element types will result in an error. Note that `FlatList` is accepted as it renders to a host `ScrollView` element.

Scroll interaction should match the `ScrollView` element direction:

- for a vertical scroll view (default or `horizontal={false}`), you should pass only the `y` option (and optionally also `momentumY`).
- for a horizontal scroll view (`horizontal={true}`), you should pass only the `x` option (and optionally `momentumX`).

Each scroll interaction consists of a mandatory drag scroll part, which simulates the user dragging the scroll view with his finger (the `y` or `x` option). This may optionally be followed by a momentum scroll movement, which simulates the inertial movement of scroll view content after the user lifts his finger (`momentumY` or `momentumX` options).

### Options \{#scroll-to-options}

- `y` - target vertical drag scroll offset
- `x` - target horizontal drag scroll offset
- `momentumY` - target vertical momentum scroll offset
- `momentumX` - target horizontal momentum scroll offset
- `contentSize` - passed to `ScrollView` events and enabling `FlatList` updates
- `layoutMeasurement` - passed to `ScrollView` events and enabling `FlatList` updates

User Event will generate several intermediate scroll steps to simulate user scroll interaction. You should not rely on exact number or values of these scrolls steps as they might be change in the future version.

This function will remember where the last scroll ended, so subsequent scroll interaction will starts from that position. The initial scroll position will be assumed to be `{ y: 0, x: 0 }`.

To simulate a `FlatList` (and other controls based on `VirtualizedList`) scrolling, you should pass the `contentSize` and `layoutMeasurement` options, which enable the underlying logic to update the currently visible window.

### Sequence of events \{#scroll-sequence}

The sequence of events depends on whether the scroll includes an optional momentum scroll component.

**Drag scroll**:

- `contentSizeChange`
- `scrollBeginDrag`
- `scroll` (multiple events)
- `scrollEndDrag`

**Momentum scroll (optional)**:

- `momentumScrollBegin`
- `scroll` (multiple events)
- `momentumScrollEnd`

## `pullToRefresh()` \{#pull-to-refresh}

:::note
Available since React Native Testing Library 14.1.0.
:::

```ts
pullToRefresh(
  instance: TestInstance,
): Promise<void>
```

Example

```ts
const user = userEvent.setup();
await user.pullToRefresh(scrollView);
```

Simulates a user performing the pull-to-refresh gesture on a host `ScrollView` element, invoking the `onRefresh` handler of its `refreshControl` prop.

This function supports only host `ScrollView` elements, passing other element types will result in an error. Note that `FlatList` and `SectionList` are accepted as they render to a host `ScrollView` element.

If the element has no `refreshControl` prop, or its `RefreshControl` has no `onRefresh` handler, the call resolves without doing anything.

## `accessibilityAction()`

:::note
Available since React Native Testing Library 14.1.0.
:::

```ts
accessibilityAction(
  instance: TestInstance,
  actionName: string,
): Promise<void>
```

Example

```ts
const user = userEvent.setup();
await user.accessibilityAction(slider, 'increment');
```

Simulates an assistive technology (e.g. a screen reader) triggering the named accessibility action on a given element, invoking its `onAccessibilityAction` handler.

The `actionName` autocompletes the [standard React Native actions](https://reactnative.dev/docs/accessibility#accessibility-actions) (`activate`, `increment`, `decrement`, `longpress`, `magicTap`, `escape`, `expand`, `collapse`), but any custom action name is accepted.

Just like a real assistive technology, the action must be reachable by the user, otherwise an error is thrown:

- the action must be declared in the element's `accessibilityActions` prop,
- the element must not be disabled (via `aria-disabled` or `accessibilityState={{ disabled: true }}`).

### Sequence of events

- `accessibilityAction`



---
url: /react-native-testing-library/docs/api/misc/accessibility.md
---

# Accessibility

## Accessible name \{#accessible-name}

The **accessible name** is the text that assistive technologies (like screen readers) announce for an element. It answers the question: _"what would a screen reader call this element?"_. For instance, a button showing a trash icon might have the accessible name `"Delete"`, so a screen reader user knows what it does.

Testing Library uses the accessible name in the [`*ByRole`](/react-native-testing-library/docs/api/queries.md#by-role) queries `name` option and in the [`toHaveAccessibleName()`](/react-native-testing-library/docs/api/jest-matchers.md#tohaveaccessiblename) matcher. Combining a role with an accessible name is a **semantic query**: it finds an element by what it is (`button`) and what it is called (`"Delete"`), rather than by its implementation details. This matches how users experience your UI.

### How the accessible name is computed \{#how-it-is-computed}

The accessible name is the **first** of these sources to produce a non-empty value. The sources are ordered from highest to lowest importance. An explicit accessibility label that the developer assigned always takes precedence, and the element's own text content is only used when no such label is present.

1. **`aria-labelledby` / `accessibilityLabelledBy`**: the [`nativeID`](https://reactnative.dev/docs/view#nativeid) (or array of IDs) of other elements, whose text content is joined with spaces in the referenced order.
2. **`aria-label` / `accessibilityLabel`**: an explicit label string on the element.
3. **`alt`** (`Image` only): the image's alternative text.
4. **`placeholder`** (`TextInput` only).
5. **Text content**: the element's own text, collected recursively from its children (see [Text content](#text-content) below). This is the fallback when none of the label sources above are set.

### Text content \{#text-content}

When falling back to text content, child text is joined as follows:

- Adjacent inline text (directly inside `Text` elements) is concatenated **without** a separator, matching how it renders on screen.
- Text from separate, non-inline elements, such as sibling `View`s, is joined with a **single space**.

For example:

```jsx
render(
  <Pressable accessibilityRole="button">
    <Text>Hello</Text>
    <Text> World</Text>
  </Pressable>,
);
// Accessible name: "Hello World"
```

### Examples \{#accessible-name-examples}

```jsx
// Explicit label wins over text content
<Pressable accessibilityRole="button" accessibilityLabel="Close dialog">
  <Text>X</Text>
</Pressable>
// Accessible name: "Close dialog"

// Text content is used when no explicit label is set
<Pressable accessibilityRole="button">
  <Text>Submit</Text>
</Pressable>
// Accessible name: "Submit"
```

## `isHiddenFromAccessibility`

```ts
function isHiddenFromAccessibility(instance: TestInstance | null): boolean {}
```

Also available as `isInaccessible()` alias for React Testing Library compatibility.

Checks if given element is hidden from assistive technology, e.g. screen readers.

:::note
Like [`isInaccessible`](https://testing-library.com/docs/dom-testing-library/api-accessibility/#isinaccessible) function from DOM Testing Library this function considers both accessibility elements and presentational elements (regular `View`s) to be accessible, unless they are hidden in terms of host platform.

This covers only part of [ARIA notion of Accessibility Tree](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion), as ARIA excludes both hidden and presentational elements from the Accessibility Tree.
:::

For the scope of this function, element is inaccessible when it, or any of its ancestors, meets any of the following conditions:

- it has `display: none` style
- it has [`aria-hidden`](https://reactnative.dev/docs/accessibility#aria-hidden) prop set to `true`
- it has [`accessibilityElementsHidden`](https://reactnative.dev/docs/accessibility#accessibilityelementshidden-ios) prop set to `true`
- it has [`importantForAccessibility`](https://reactnative.dev/docs/accessibility#importantforaccessibility-android) prop set to `no-hide-descendants`
- it has sibling host element with either [`aria-modal`](https://reactnative.dev/docs/accessibility#aria-modal-ios) or [`accessibilityViewIsModal`](https://reactnative.dev/docs/accessibility#accessibilityviewismodal-ios) prop set to `true`

Specifying `accessible={false}`, `role="none"`, `accessibilityRole="none"`, or `importantForAccessibility="no"` props does not cause the element to become inaccessible.



---
url: /react-native-testing-library/docs/api/misc/async.md
---

# Async utilities

## `findBy*` queries

The `findBy*` queries are used to find elements that are not instantly available but will be added as a result of some asynchronous action. Learn more details [here](/react-native-testing-library/docs/api/queries.md#find-by).

## `waitFor`

```tsx
function waitFor<T>(
  expectation: () => T,
  options?: {
    timeout?: number;
    interval?: number;
    onTimeout?: (error: Error) => Error;
  },
): Promise<T>;
```

Waits for the `expectation` callback to pass. `waitFor` runs the callback multiple times until timeout is reached, as specified by the `timeout` and `interval` options. The callback must throw an error when the expectation is not met. Returning any value, including a falsy one, is treated as meeting the expectation, and the callback result is returned to the caller.

```tsx
await waitFor(() => expect(mockFunction).toHaveBeenCalledWith());
```

`waitFor` executes the `expectation` callback every `interval` (default: 50 ms) until `timeout` (default: 1000 ms) is reached. Execution stops as soon as the callback doesn't throw an error, and the callback's return value is returned to the caller. If timeout is reached, `waitFor` re-throws the final error thrown by `expectation`.

```tsx
// ❌ `waitFor` will return immediately because callback does not throw
await waitFor(() => false);
```

`waitFor` is an async function so you need to `await` the result to pause test execution.

```jsx
// ❌ missing `await`: `waitFor` will just return Promise that will be rejected when the timeout is reached
waitFor(() => expect(1).toBe(2));
```

:::note
You can enforce awaiting `waitFor` by using the [await-async-utils](https://github.com/testing-library/eslint-plugin-testing-library/blob/main/docs/rules/await-async-utils.md) rule from [eslint-plugin-testing-library](https://github.com/testing-library/eslint-plugin-testing-library).
:::

Since `waitFor` runs the `expectation` callback multiple times, [avoid performing side effects](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#performing-side-effects-in-waitfor) in `waitFor`.

```jsx
await waitFor(async () => {
  // ❌ button will be pressed on each waitFor iteration
  await fireEvent.press(screen.getByText('press me'));
  expect(mockOnPress).toHaveBeenCalled();
});
```

:::note
Avoiding side effects in `expectation` callback can be partially enforced with the [`no-wait-for-side-effects` rule](https://github.com/testing-library/eslint-plugin-testing-library/blob/main/docs/rules/no-wait-for-side-effects.md).
:::

Use a [single assertion per `waitFor`](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#having-multiple-assertions-in-a-single-waitfor-callback) for consistency and faster failing tests. For multiple assertions, use separate `waitFor` calls. Often you won't need to wrap the second assertion in `waitFor` since the first one waits for the asynchronous change.

`waitFor` checks whether Jest fake timers are enabled and adapts its behavior in such case. The following snippet is a simplified version of how it behaves when fake timers are enabled:

```tsx
let fakeTimeRemaining = timeout;
let lastError;

while (fakeTimeRemaining > 0) {
  fakeTimeRemaining = fakeTimeRemaining - interval;
  jest.advanceTimersByTime(interval);
  try {
    // resolve
    return expectation();
  } catch (error) {
    lastError = error;
  }
}

// reject
throw lastError;
```

In the following example we test that a function is called after 10 seconds using fake timers. With fake timers, the test doesn't depend on real time passing, making it faster and more reliable. We don't need to advance fake timers through Jest's API because `waitFor` handles this.

```tsx
// in component
setTimeout(() => {
  someFunction();
}, 10000);

// in test
jest.useFakeTimers();

await waitFor(
  () => {
    expect(someFunction).toHaveBeenCalledWith();
  },
  { timeout: 10000 },
);
```

:::note
If you receive warnings related to `act()` function consult our [Understanding Act](/react-native-testing-library/docs/advanced/understanding-act.md) function document.
:::

### Options

- `timeout`: How long to wait for, in ms. Defaults to 1000 ms (configured by `asyncUtilTimeout` option).
- `interval`: How often to check, in ms. Defaults to 50 ms.
- `onTimeout`: Callback to transform the error before it's thrown. Useful for debugging, e.g., `onTimeout: () => { screen.debug(); }`.

## `waitForElementToBeRemoved`

```ts
function waitForElementToBeRemoved<T>(
  expectation: () => T,
  options?: {
    timeout?: number;
    interval?: number;
    onTimeout?: (error: Error) => Error;
  },
): Promise<T>;
```

Waits for non-deterministic periods of time until queried element is removed or times out. `waitForElementToBeRemoved` periodically calls `expectation` every `interval` milliseconds to determine whether the element has been removed or not.

```jsx
import { render, screen, waitForElementToBeRemoved } from '@testing-library/react-native';

test('waiting for an Banana to be removed', async () => {
  await render(<Banana />);

  await waitForElementToBeRemoved(() => screen.getByText('Banana ready'));
});
```

This method expects that the element is initially present in the render tree and then is removed from it. If the element is not present when you call this method it throws an error.

You can use any of `getBy`, `getAllBy`, `queryBy` and `queryAllBy` queries for `expectation` parameter.

:::note
If you receive warnings related to `act()` function consult our [Understanding Act](/react-native-testing-library/docs/advanced/understanding-act.md) function document.
:::



---
url: /react-native-testing-library/docs/api/misc/config.md
---

# Configuration

## `configure`

```ts
type Config = {
  /** Default timeout, in ms, for `waitFor` and `findBy*` queries. */
  asyncUtilTimeout: number;

  /** Default value for `includeHiddenElements` query option. */
  defaultIncludeHiddenElements: boolean;

  /** Default options for `debug` helper. */
  defaultDebugOptions?: Partial<DebugOptions>;
};

type ConfigAliasOptions = {
  /** RTL-compatibility alias for `defaultIncludeHiddenElements`. */
  defaultHidden: boolean;
};

function configure(options: Partial<Config & ConfigAliasOptions>) {}
```

### `asyncUtilTimeout` option

Default timeout, in ms, for async helper functions (`waitFor`, `waitForElementToBeRemoved`) and `findBy*` queries. Defaults to 1000 ms.

### `defaultIncludeHiddenElements` option

Default value for [includeHiddenElements](/react-native-testing-library/docs/api/queries.md#includehiddenelements-option) query option for all queries. The default value is set to `false`, so all queries will not match [elements hidden from accessibility](#ishiddenfromaccessibility). This is because the users of the app would not be able to see such elements.

This option is also available as `defaultHidden` alias for compatibility with [React Testing Library](https://testing-library.com/docs/dom-testing-library/api-configuration/#defaulthidden).

### `defaultDebugOptions` option

Default [debug options](#debug) to be used when calling `debug()`. These default options will be overridden by the ones you specify directly when calling `debug()`.

## `resetToDefaults()`

```ts
function resetToDefaults() {}
```

## Environment variables

### `RNTL_SKIP_AUTO_CLEANUP`

Set to `true` to disable automatic `cleanup()` after each test. It works the same as importing `react-native-testing-library/dont-cleanup-after-each` or using `react-native-testing-library/pure`.

```shell
$ RNTL_SKIP_AUTO_CLEANUP=true jest
```

### `RNTL_SKIP_AUTO_DETECT_FAKE_TIMERS`

Set to `true` to disable auto-detection of fake timers. This might be useful in rare cases when you want to use non-Jest fake timers. See [issue #886](https://github.com/callstack/react-native-testing-library/issues/886) for more details.

```shell
$ RNTL_SKIP_AUTO_DETECT_FAKE_TIMERS=true jest
```



---
url: /react-native-testing-library/docs/api/misc/other.md
---

# Other helpers

## `within` \{#within}

```jsx
function within(instance: TestInstance): Queries {}
```

`within` performs [queries](/react-native-testing-library/docs/api/queries.md) scoped to given element.

:::note
Please note that additional `render` specific operations like `rerender`, `unmount`, `debug`, `toJSON` are _not_ included.
:::

```jsx
const detailsScreen = within(screen.getByHintText('Details Screen'));
expect(detailsScreen.getByText('Some Text')).toBeOnTheScreen();
expect(detailsScreen.getByDisplayValue('Some Value')).toBeOnTheScreen();
expect(detailsScreen.queryByLabelText('Some Label')).toBeOnTheScreen();
await expect(detailsScreen.findByHintText('Some Label')).resolves.toBeOnTheScreen();
```

Use cases for scoped queries include:

- queries scoped to a single item inside a FlatList containing many items
- queries scoped to a single screen in tests involving screen transitions (e.g. with react-navigation)

## `act`

```ts
function act<T>(callback: () => T | Promise<T>): Promise<T>;
```

Wraps code that causes React state updates to ensure all updates are processed before assertions. By default any `render`, `rerender`, `fireEvent`, and `waitFor` calls are wrapped by this function, so there is no need to wrap it manually.

**In v14, `act` is now async by default and always returns a Promise**. This works with async React features like `Suspense` boundaries and the `use()` hook. All pending React updates are executed before the Promise resolves.

```ts
import { act } from '@testing-library/react-native';

it('should update state', async () => {
  await act(() => {
    setState('new value');
  });
  expect(state).toBe('new value');
});
```

**Note**: Even if your callback is synchronous, you should still use `await act(...)` as `act` now always returns a Promise.

Consult our [Understanding Act function](/react-native-testing-library/docs/advanced/understanding-act.md) document for more understanding of its intricacies.

## `cleanup`

```ts
function cleanup(): Promise<void>;
```

Unmounts React trees that were mounted with `render` and clears `screen` variable that holds latest `render` output.

:::info
Please note that this is done automatically if the testing framework you're using supports the `afterEach` global (like mocha, Jest, and Jasmine). If not, you will need to do manual cleanups after each test.
:::

For example, if you're using the `jest` testing framework, then you would need to use the `afterEach` hook like so:

```jsx
import { cleanup, render } from '@testing-library/react-native/pure';
import { View } from 'react-native';

afterEach(async () => {
  await cleanup();
});

it('renders a view', async () => {
  await render(<View />);
  // ...
});
```

The `afterEach(cleanup)` call also works in `describe` blocks:

```jsx
describe('when logged in', () => {
  afterEach(async () => {
    await cleanup();
  });

  it('renders the user', async () => {
    await render(<SiteHeader />);
    // ...
  });
});
```

Failing to call `cleanup` when you've called `render` could result in a memory leak and tests which are not "idempotent" (which can lead to difficult to debug errors in your tests).



---
url: /react-native-testing-library/docs/api/misc/render-hook.md
---

# `renderHook` function

## `renderHook`

```ts
async function renderHook<Result, Props>(
  hookFn: (props: Props) => Result,
  options?: RenderHookOptions<Props>,
): Promise<RenderHookResult<Result, Props>>;
```

Renders a test component that calls the provided `callback` (and any hooks it uses) on each render. Returns a Promise that resolves to a [`RenderHookResult`](#renderhookresult) object.

**This is the recommended default API** for testing hooks. It uses async `act` internally to ensure all pending React updates are executed during rendering. This makes it compatible with async React features like `Suspense` boundaries and the `use()` hook.

- **Returns a Promise**: Should be awaited
- **Async methods**: Both `rerender` and `unmount` return Promises and should be awaited
- **Suspense support**: Compatible with `Suspense` boundaries and `use()` hook

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

it('should increment count', async () => {
  const { result } = await renderHook(() => useCount());

  expect(result.current.count).toBe(0);
  await act(() => {
    // Note that you should wrap the calls to functions your hook returns with `act` if they trigger an update of your hook's state to ensure pending useEffects are run before your next assertion.
    result.current.increment();
  });
  expect(result.current.count).toBe(1);
});
```

```ts
// useCount.js
import { useState } from 'react';

export const useCount = () => {
  const [count, setCount] = useState(0);
  const increment = () => setCount((previousCount) => previousCount + 1);

  return { count, increment };
};
```

The `renderHook` function accepts the following arguments:

**Callback**: A function called on each render of the test component. This function should call one or more hooks for testing.

The callback receives `props` from the `initialProps` option, or from a subsequent `rerender` call if provided.

### `options`

A `RenderHookOptions<Props>` object with the following properties:

#### `initialProps` \{#initial-props}

The initial values to pass as `props` to the `callback` function of `renderHook`. The `Props` type is determined by the type passed to or inferred by the `renderHook` call.

#### `wrapper`

A React component that wraps the test component. Use this to add context providers so hooks can access them with `useContext`.

### Result

```ts
interface RenderHookResult<Result, Props> {
  result: { current: Result };
  rerender: (props: Props) => Promise<void>;
  unmount: () => Promise<void>;
}
```

The `renderHook` function returns a Promise that resolves to an object with the following properties:

#### `result`

The `current` value contains whatever the `callback` returned from `renderHook`. The `Result` type is determined by the type passed to or inferred by the `renderHook` call.

**Note:** When using React Suspense, `result.current` will be `null` while the hook is suspended.

#### `rerender`

An async function that rerenders the test component and recalculates hooks. If `newProps` are passed, they replace the `callback` function's `initialProps` for subsequent rerenders. The `Props` type is determined by the type passed to or inferred by the `renderHook` call.

**Note**: This method returns a Promise and should be awaited.

#### `unmount`

An async function to unmount the test component. This is commonly used to trigger cleanup effects for `useEffect` hooks.

**Note**: This method returns a Promise and should be awaited.

### Examples

Additional examples of using `renderHook`:

#### With `initialProps`

```ts
import { useState, useEffect } from 'react';
import { renderHook, act } from '@testing-library/react-native';

const useCount = (initialCount: number) => {
  const [count, setCount] = useState(initialCount);
  const increment = () => setCount((previousCount) => previousCount + 1);

  useEffect(() => {
    setCount(initialCount);
  }, [initialCount]);

  return { count, increment };
};

it('should increment count', async () => {
  const { result, rerender } = await renderHook((initialCount: number) => useCount(initialCount), {
    initialProps: 1,
  });

  expect(result.current.count).toBe(1);

  await act(() => {
    result.current.increment();
  });

  expect(result.current.count).toBe(2);
  await rerender(5);
  expect(result.current.count).toBe(5);
});
```

#### With `wrapper`

```tsx
it('should use context value', async () => {
  function Wrapper({ children }: { children: ReactNode }) {
    return <Context.Provider value="provided">{children}</Context.Provider>;
  }

  const { result } = await renderHook(() => useHook(), { wrapper: Wrapper });
  // ...
});
```

#### With React Suspense

```tsx
import { renderHook, act } from '@testing-library/react-native';
import { Text } from 'react-native';

function useSuspendingHook(promise: Promise<string>) {
  return React.use(promise);
}

it('handles hook with suspense', async () => {
  let resolvePromise: (value: string) => void;
  const promise = new Promise<string>((resolve) => {
    resolvePromise = resolve;
  });

  const { result } = await renderHook(useSuspendingHook, {
    initialProps: promise,
    wrapper: ({ children }) => (
      <React.Suspense fallback={<Text>Loading...</Text>}>{children}</React.Suspense>
    ),
  });

  // Initially suspended, result should not be available
  expect(result.current).toBeNull();

  await act(() => resolvePromise('resolved'));
  expect(result.current).toBe('resolved');
});
```



---
url: /react-native-testing-library/docs/guides/how-to-query.md
---

# How should I query?

React Native Testing Library provides various query types for finding views in tests. The number of queries can be confusing. This guide helps you pick the right queries for your test scenarios.

## Query parts

Each query is composed of two parts: variant and predicate, which are separated by the `by` word in the middle of the query.

Consider the following query:

```ts
getByRole();
```

For this query, `getBy*` is the query variant, and `*ByRole` is the predicate.

## Query variant

The query variants describe the expected number (and timing) of matching elements, so they differ in their return type.

| Variant                                                                         | Assertion                     | Return type                       | Is Async? |
| ------------------------------------------------------------------------------- | ----------------------------- | --------------------------------- | --------- |
| [`getBy*`](/react-native-testing-library/docs/api/queries.md#get-by)            | Exactly one matching element  | `TestInstance`                    | No        |
| [`getAllBy*`](/react-native-testing-library/docs/api/queries.md#get-all-by)     | At least one matching element | `Array<TestInstance>`             | No        |
| [`queryBy*`](/react-native-testing-library/docs/api/queries.md#query-by)        | Zero or one matching element  | <code>TestInstance \| null</code> | No        |
| [`queryAllBy*`](/react-native-testing-library/docs/api/queries.md#query-all-by) | No assertion                  | `Array<TestInstance>`             | No        |
| [`findBy*`](/react-native-testing-library/docs/api/queries.md#find-by)          | Exactly one matching element  | `Promise<TestInstance>`           | Yes       |
| [`findAllBy*`](/react-native-testing-library/docs/api/queries.md#find-all-by)   | At least one matching element | `Promise<Array<TestInstance>>`    | Yes       |

Queries work as implicit assertions on the number of matching elements and will throw an error when the assertion fails.

### Idiomatic query variants

Idiomatic query variants clarify test intent and the expected number of matching elements. They will also throw helpful errors if assertions fail to help diagnose the issues.

Here are general guidelines for picking idiomatic query variants:

1. Use `getBy*` in the most common case when you expect a **single matching element**. Use other queries only in more specific cases.
2. Use `findBy*` when an element is not yet in the element tree, but you expect it to be there as a **result of some asynchronous action**.
3. Use `getAllBy*` (and `findAllBy*` for async) if you expect **more than one matching element**, e.g. in a list.
4. Use `queryBy*` only when element **should not exist** to use it together with e.g. [`not.toBeOnTheScreen()`](/react-native-testing-library/docs/api/jest-matchers.md#tobeonthescreen) matcher.

Avoid using `queryAllBy*` in regular tests, as it provides no assertions on the number of matching elements. You may still find it useful when building reusable custom testing tools.

## Query predicate

The query predicate describes how you decide whether to match the given element.

| Predicate                                                                                     | Supported elements | Inspected props                                                                                                   |
| --------------------------------------------------------------------------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| [`*ByRole`](/react-native-testing-library/docs/api/queries.md#by-role)                        | all host elements  | `role`, `accessibilityRole`,<br /> optional: accessible name, accessibility state and value                       |
| [`*ByLabelText`](/react-native-testing-library/docs/api/queries.md#by-label-text)             | all host elements  | `aria-label`, `aria-labelledby`,<br /> `accessibilityLabel`, `accessibilityLabelledBy`,<br /> `alt` (for `Image`) |
| [`*ByDisplayValue`](/react-native-testing-library/docs/api/queries.md#by-display-value)       | `TextInput`        | `value`, `defaultValue`                                                                                           |
| [`*ByPlaceholderText`](/react-native-testing-library/docs/api/queries.md#by-placeholder-text) | `TextInput`        | `placeholder`                                                                                                     |
| [`*ByText`](/react-native-testing-library/docs/api/queries.md#by-text)                        | `Text`             | `children` (text content)                                                                                         |
| [`*ByHintText`](/react-native-testing-library/docs/api/queries.md#by-hint-text)               | all host elements  | `accessibilityHint`                                                                                               |
| [`*ByTestId`](/react-native-testing-library/docs/api/queries.md#by-test-id)                   | all host elements  | `testID`                                                                                                          |

### Idiomatic query predicates

Choosing the right query predicate helps express test intent and makes tests resemble how users interact with your code (components, screens, etc.), following our [Guiding Principles](https://testing-library.com/docs/guiding-principles). Most predicates also promote proper accessibility props, which add a semantic layer on top of an element tree composed primarily of [`View`](https://reactnative.dev/docs/view) elements.

Use query predicates in the following order of priority:

### 1. By Role query \{#by-role-query}

The [`*ByRole`](/react-native-testing-library/docs/api/queries.md#by-role) predicate starts with the semantic role of the element and can be narrowed down with additional options. React Native has two role systems: the web/ARIA-compatible one based on [`role`](https://reactnative.dev/docs/accessibility#role) prop and the traditional one based on [`accessibilityRole`](https://reactnative.dev/docs/accessibility#accessibilityrole) prop. You can use either.

In most cases, you need to set accessibility roles explicitly (or your component library can set some of them for you). These roles allow assistive technologies (like screen readers) and testing code to understand your view hierarchy better.

Some frequently used roles include:

- `alert` - important text to be presented to the user, e.g., error message
- `button`
- `checkbox` & `switch` - on/off controls
- `heading` (`header`) - header for content section, e.g., the title of navigation bar
- `img` (`image`)
- `link`
- `menu` & `menuitem`
- `progressbar`
- `radiogroup` & `radio`
- `searchbox` (`search`)
- `slider` (`adjustable`)
- `summary`
- `tablist` & `tab`
- `text` - static text that cannot change
- `toolbar` - container for action buttons

#### Name option \{#by-role-query-name-option}

Frequently, you will want to add the [`name`](/react-native-testing-library/docs/api/queries.md#by-role-options) option, which will match both the element's role and its accessible name (= element's accessibility label or text content).

Here are a couple of examples:

- start button: `getByRole("button", { name: "Start" })`
- silent mode switch: `getByRole("switch", { name: "Silent Mode" })`
- screen header: `getByRole("header", { name: "Settings" })`
- undo menu item: `getByRole("menuitem", { name: "Undo" })`
- error messages: `getByRole("alert", { name: /Not logged in/ })`

### 2. Text input queries \{#text-input-queries}

Querying [`TextInput`](https://reactnative.dev/docs/textinput) elements presents a unique challenge as there is no separate role for `TextInput` elements. There is a `searchbox`/`search` role, which can be assigned to `TextInput`, but it should be only used in the context of search inputs, leaving other text inputs without a role to query with.

Therefore, you can use the following queries to find relevant text inputs:

1. [`*ByLabelText`](/react-native-testing-library/docs/api/queries.md#by-label-text) - will match the accessibility label of the element. This query will match any host elements, including `TextInput` elements.
2. [`*ByPlaceholderText`](/react-native-testing-library/docs/api/queries.md#by-placeholder-text) - will match the placeholder of `TextInput` element. This query will match only `TextInput` elements.
3. [`*ByDisplayValue`](/react-native-testing-library/docs/api/queries.md#by-display-value) - will the current (or default) value of `TextInput` element. This query will match only `TextInput` elements.

### 3. Other accessible queries \{#other-accessible-queries}

These queries reflect the apps' user experience, both visual and through assistive technologies (e.g. screen reader).

These queries include:

- [`*ByText`](/react-native-testing-library/docs/api/queries.md#by-text) - will match the text content of the element. This query will match only `Text` elements.
- [`*ByLabelText`](/react-native-testing-library/docs/api/queries.md#by-label-text) - will match the accessibility label of the element.
- [`*ByHintText`](/react-native-testing-library/docs/api/queries.md#by-hint-text) - will match the accessibility hint of the element.

### 4. Test ID query \{#test-id-query}

As a final predicate, you can use the `testID` prop to find relevant views. Using the [`*ByTestId`](/react-native-testing-library/docs/api/queries.md#by-test-id) predicate offers the most flexibility, but at the same time, it does not represent the user experience, as users are not aware of test IDs.

Note that using test IDs is common in end-to-end testing due to various issues with querying views through other means **in that specific context**. For integration and component tests, use the recommended RNTL queries to make tests more reliable and resilient.



---
url: /react-native-testing-library/docs/guides/common-mistakes.md
---

# Common Mistakes with React Native Testing Library

> **Note:** This guide is adapted from Kent C. Dodds' article ["Common mistakes with React Testing Library"](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library) for React Native Testing Library v14. The original article focuses on web React, but the principles apply to React Native as well. This adaptation includes React Native-specific examples, async API usage (v14), and ARIA-compatible accessibility attributes.

React Native Testing Library guiding principle is:

> "The more your tests resemble the way your software is used, the more confidence they can give you."

This guide outlines some common mistakes people make when using React Native Testing Library and how to avoid them.

## Using the wrong query \{#using-the-wrong-query}

Importance: high

React Native Testing Library provides several query types. Here's the priority order:

1. **Queries that reflect user experience:**
   - `getByRole` - most accessible
   - `getByLabelText` - accessible label
   - `getByPlaceholderText` - `TextInput` placeholder text
   - `getByText` - text content
   - `getByDisplayValue` - `TextInput` input value

2. **Semantic queries:**
   - `getByTestId` - only if nothing else works

Here's an example of using the right query:

```tsx
import { TextInput, View } from 'react-native';
import { render, screen } from '@testing-library/react-native';

test('finds input by label', async () => {
  await render(
    <View>
      <TextInput aria-label="Username" placeholder="Enter username" value="" />
    </View>,
  );

  // ✅ Good - uses accessible label
  const input = screen.getByLabelText('Username');

  // ✅ Also good - uses placeholder
  const inputByPlaceholder = screen.getByPlaceholderText('Enter username');

  // ❌ Bad - uses testID when accessible queries work
  // const input = screen.getByTestId('username-input');
});
```

## Not using `*ByRole` query most of the time \{#not-using-byrole-most-of-the-time}

Importance: high

`getByRole` is the most accessible query and should be your first choice. It queries elements by their semantic role:

```tsx
import { Pressable, Text, TextInput, View } from 'react-native';
import { render, screen } from '@testing-library/react-native';

test('uses role queries', async () => {
  await render(
    <View>
      <Pressable role="button">
        <Text>Submit</Text>
      </Pressable>
      <TextInput role="searchbox" aria-label="Search" placeholder="Search..." />
    </View>,
  );

  // ✅ Good - uses role query
  const button = screen.getByRole('button', { name: 'Submit' });
  const searchbox = screen.getByRole('searchbox', { name: 'Search' });

  expect(button).toBeOnTheScreen();
  expect(searchbox).toBeOnTheScreen();
});
```

Common roles in React Native include:

- `button` - pressable elements
- `text` - static text
- `header` / `heading` - headers
- `searchbox` - search inputs
- `switch` - toggle switches
- `checkbox` - checkboxes
- `radio` - radio buttons
- And more...

Note: React Native supports both ARIA-compatible (`role`) and legacy (`accessibilityRole`) props. Prefer `role` for consistency with web standards.

## Using the wrong assertion \{#using-the-wrong-assertion}

Importance: high

React Native Testing Library provides built-in Jest matchers. Make sure you're using the right ones:

```tsx
import { Pressable, Text } from 'react-native';
import { render, screen } from '@testing-library/react-native';

test('button is disabled', async () => {
  await render(
    <Pressable role="button" aria-disabled>
      <Text>Submit</Text>
    </Pressable>,
  );

  const button = screen.getByRole('button', { name: 'Submit' });

  // ✅ Good - uses RNTL matcher
  expect(button).toBeDisabled();

  // ❌ Bad - doesn't use RNTL matcher
  expect(button.props['aria-disabled']).toBe(true);
});
```

Common matchers include:

- `toBeOnTheScreen()` - checks if element is rendered (replaces `toBeInTheDocument()`)
- `toBeDisabled()` - checks if element is disabled
- `toHaveTextContent()` - checks text content
- `toHaveAccessibleName()` - checks accessible name
- And more...

## Using `query*` variants for anything except checking for non-existence \{#using-query-variants-for-anything-except-checking-for-non-existence}

Importance: high

Use `queryBy*` only when checking that an element doesn't exist:

```tsx
import { View, Text } from 'react-native';
import { render, screen } from '@testing-library/react-native';

test('checks non-existence', async () => {
  await render(
    <View>
      <Text>Hello</Text>
    </View>,
  );

  // ✅ Good - uses queryBy for non-existence check
  expect(screen.queryByText('Goodbye')).not.toBeOnTheScreen();

  // ❌ Bad - uses queryBy when element should exist
  // const element = screen.queryByText('Hello');
  // expect(element).toBeOnTheScreen();

  // ✅ Good - uses getBy when element should exist
  expect(screen.getByText('Hello')).toBeOnTheScreen();
});
```

## Using `waitFor` to wait for elements that can be queried with `find*` \{#using-waitfor-to-wait-for-elements-that-can-be-queried-with-find}

Importance: high

Use `findBy*` queries instead of `waitFor` + `getBy*`:

```tsx
import { View, Text } from 'react-native';
import { render, screen, waitFor } from '@testing-library/react-native';

test('waits for element', async () => {
  const Component = () => {
    const [show, setShow] = React.useState(false);

    React.useEffect(() => {
      setTimeout(() => setShow(true), 100);
    }, []);

    return <View>{show && <Text>Loaded</Text>}</View>;
  };

  await render(<Component />);

  // ✅ Good - uses findBy query
  const element = await screen.findByText('Loaded');
  expect(element).toBeOnTheScreen();

  // ❌ Bad - uses waitFor + getBy
  // await waitFor(() => {
  //   expect(screen.getByText('Loaded')).toBeOnTheScreen();
  // });
});
```

## Performing side-effects in `waitFor` \{#performing-side-effects-in-waitfor}

Importance: high

Don't perform side-effects in `waitFor` callbacks:

```tsx
import { Pressable, Text, View } from 'react-native';
import { render, screen, waitFor, fireEvent } from '@testing-library/react-native';

test('avoids side effects in waitFor', async () => {
  const Component = () => {
    const [count, setCount] = React.useState(0);
    return (
      <View>
        <Pressable role="button" onPress={() => setCount(count + 1)}>
          <Text>Increment</Text>
        </Pressable>
        <Text>Count: {count}</Text>
      </View>
    );
  };

  await render(<Component />);

  const button = screen.getByRole('button');

  // ❌ Bad - side effect in waitFor
  // await waitFor(async () => {
  //   await fireEvent.press(button);
  //   expect(screen.getByText('Count: 1')).toBeOnTheScreen();
  // });

  // ✅ Good - side effect outside waitFor
  await fireEvent.press(button);
  await waitFor(() => {
    expect(screen.getByText('Count: 1')).toBeOnTheScreen();
  });
});
```

## Using `container` to query for elements \{#using-container-to-query-for-elements}

Importance: high

React Native Testing Library provides a `container` object that has a `queryAll` method, but you should avoid using it directly:

```tsx
import { View, Text } from 'react-native';
import { render } from '@testing-library/react-native';

test('finds element incorrectly', async () => {
  const { container } = await render(
    <View>
      <Text testID="message">Hello</Text>
    </View>,
  );

  // ❌ Bad - using container.queryAll directly
  const element = container.queryAll((node) => node.props.testID === 'message')[0];

  // ✅ Good - use proper queries
  // const element = screen.getByTestId('message');
});
```

Instead, use the proper query methods from `screen` or the `render` result. The `container` is a low-level API that you rarely need.

## Passing an empty callback to `waitFor` \{#passing-an-empty-callback-to-waitfor}

Importance: high

Don't pass an empty callback to `waitFor`:

```tsx
import { View } from 'react-native';
import { render, waitFor } from '@testing-library/react-native';

test('waits correctly', async () => {
  await render(<View testID="test" />);

  // ❌ Bad - empty callback
  // await waitFor(() => {});

  // ✅ Good - meaningful assertion
  await waitFor(() => {
    expect(screen.getByTestId('test')).toBeOnTheScreen();
  });
});
```

## Not using `screen` \{#not-using-screen}

Importance: medium

You can get all the queries from the `render` result:

```tsx
import { View, Text } from 'react-native';
import { render } from '@testing-library/react-native';

test('renders component', async () => {
  const { getByText } = await render(
    <View>
      <Text>Hello</Text>
    </View>,
  );

  expect(getByText('Hello')).toBeOnTheScreen();
});
```

But you can also get them from the `screen` object:

```tsx
import { View, Text } from 'react-native';
import { render, screen } from '@testing-library/react-native';

test('renders component', async () => {
  await render(
    <View>
      <Text>Hello</Text>
    </View>,
  );

  expect(screen.getByText('Hello')).toBeOnTheScreen();
});
```

Using `screen` has several benefits:

1. You don't need to destructure `getByText` from `render`
2. It's more consistent with the Testing Library ecosystem

## Wrapping things in `act` unnecessarily \{#wrapping-things-in-act-unnecessarily}

Importance: medium

React Native Testing Library's `render`, `renderHook`, `userEvent`, and `fireEvent` are already wrapped in `act`, so you don't need to wrap them yourself:

```tsx
import { Pressable, Text, View } from 'react-native';
import { render, fireEvent, screen } from '@testing-library/react-native';

test('updates on press', async () => {
  const Component = () => {
    const [count, setCount] = React.useState(0);
    return (
      <View>
        <Pressable role="button" onPress={() => setCount(count + 1)}>
          <Text>Count: {count}</Text>
        </Pressable>
      </View>
    );
  };

  await render(<Component />);

  const button = screen.getByRole('button');

  // ✅ Good - fireEvent is already wrapped in act
  await fireEvent.press(button);

  expect(screen.getByText('Count: 1')).toBeOnTheScreen();

  // ❌ Bad - unnecessary act wrapper
  // await act(async () => {
  //   await fireEvent.press(button);
  // });
});
```

## Not using User Event API

Importance: medium

`userEvent` provides a more realistic way to simulate user interactions:

```tsx
import { Pressable, Text, TextInput, View } from 'react-native';
import { render, screen, userEvent } from '@testing-library/react-native';

test('uses userEvent', async () => {
  const user = userEvent.setup();

  const Component = () => {
    const [value, setValue] = React.useState('');
    return (
      <View>
        <TextInput aria-label="Name" value={value} onChangeText={setValue} />
        <Pressable role="button" onPress={() => setValue('')}>
          <Text>Clear</Text>
        </Pressable>
      </View>
    );
  };

  await render(<Component />);

  const input = screen.getByLabelText('Name');
  const button = screen.getByRole('button', { name: 'Clear' });

  // ✅ Good - uses userEvent for realistic interactions
  await user.type(input, 'John');
  expect(input).toHaveValue('John');

  await user.press(button);
  expect(input).toHaveValue('');
});
```

`userEvent` methods are async and must be awaited. Available methods include:

- `press()` - simulates a press
- `longPress()` - simulates long press
- `type()` - simulates typing
- `clear()` - clears text input
- `paste()` - simulates pasting
- `scrollTo()` - simulates scrolling

## Not querying by text \{#not-querying-by-text}

Importance: medium

In React Native, text is rendered in `<Text>` components. You should query by the text content that users see:

```tsx
import { Text, View } from 'react-native';
import { render, screen } from '@testing-library/react-native';

test('finds text correctly', async () => {
  await render(
    <View>
      <Text>Hello World</Text>
    </View>,
  );

  // ✅ Good - queries by visible text
  expect(screen.getByText('Hello World')).toBeOnTheScreen();

  // ❌ Bad - queries by testID when text is available
  // expect(screen.getByTestId('greeting')).toBeOnTheScreen();
});
```

## Not using Testing Library ESLint plugins \{#not-using-testing-library-eslint-plugins}

Importance: medium

There's an ESLint plugin for Testing Library: [`eslint-plugin-testing-library`](https://github.com/testing-library/eslint-plugin-testing-library). This plugin can help you avoid common mistakes and will automatically fix your code in many cases.

You can install it with:

```bash
yarn add --dev eslint-plugin-testing-library
```

And configure it in your `eslint.config.js` (flat config):

```js
import testingLibrary from 'eslint-plugin-testing-library';

export default [testingLibrary.configs['flat/react']];
```

Note: Unlike React Testing Library, React Native Testing Library has built-in Jest matchers, so you don't need `eslint-plugin-jest-dom`.

## Using `cleanup` \{#using-cleanup}

Importance: medium

React Native Testing Library automatically cleans up after each test. You don't need to call `cleanup()` manually unless you're using the `pure` export (which doesn't include automatic cleanup).

If you want to disable automatic cleanup for a specific test, you can use:

```tsx
import { render } from '@testing-library/react-native/pure';

test('does not cleanup', async () => {
  // This test won't cleanup automatically
  await render(<MyComponent />);
  // ... your test
});
```

But in most cases, you don't need to worry about cleanup at all - it's handled automatically.

## Using `get*` variants as assertions \{#using-get-variants-as-assertions}

Importance: low

`getBy*` queries throw errors when elements aren't found, so they work as assertions. However, for better error messages, you might want to combine them with explicit matchers:

```tsx
import { View, Text } from 'react-native';
import { render, screen } from '@testing-library/react-native';

test('uses getBy as assertion', async () => {
  await render(
    <View>
      <Text>Hello</Text>
    </View>,
  );

  // ✅ Good - getBy throws if not found, so it's an assertion
  const element = screen.getByText('Hello');
  expect(element).toBeOnTheScreen();

  // ✅ Also good - more explicit
  expect(screen.getByText('Hello')).toBeOnTheScreen();

  // ❌ Bad - redundant assertion
  // const element = screen.getByText('Hello');
  // expect(element).not.toBeNull(); // getBy already throws if null
});
```

## Having multiple assertions in a single `waitFor` callback \{#having-multiple-assertions-in-a-single-waitfor-callback}

Importance: low

Keep `waitFor` callbacks focused on a single assertion:

```tsx
import { View, Text } from 'react-native';
import { render, screen, waitFor } from '@testing-library/react-native';

test('waits with single assertion', async () => {
  const Component = () => {
    const [count, setCount] = React.useState(0);

    React.useEffect(() => {
      setTimeout(() => setCount(1), 100);
    }, []);

    return (
      <View>
        <Text>Count: {count}</Text>
      </View>
    );
  };

  await render(<Component />);

  // ✅ Good - single assertion per waitFor
  await waitFor(() => {
    expect(screen.getByText('Count: 1')).toBeOnTheScreen();
  });

  // If you need multiple assertions, do them after waitFor
  expect(screen.getByText('Count: 1')).toHaveTextContent('Count: 1');

  // ❌ Bad - multiple assertions in waitFor
  // await waitFor(() => {
  //   expect(screen.getByText('Count: 1')).toBeOnTheScreen();
  //   expect(screen.getByText('Count: 1')).toHaveTextContent('Count: 1');
  // });
});
```

## Using `wrapper` as the variable name \{#using-wrapper-as-the-variable-name}

Importance: low

This is not really a "mistake" per se, but it's a common pattern that can be improved. When you use the `wrapper` option in `render`, you might be tempted to name your wrapper component `Wrapper`:

```tsx
import { View } from 'react-native';
import { render, screen } from '@testing-library/react-native';

test('renders with wrapper', async () => {
  const Wrapper = ({ children }: { children: React.ReactNode }) => (
    <View testID="wrapper">{children}</View>
  );

  await render(<View testID="content">Content</View>, {
    wrapper: Wrapper,
  });

  expect(screen.getByTestId('content')).toBeOnTheScreen();
});
```

This works fine, but it's more conventional to name it something more descriptive like `ThemeProvider` or `AllTheProviders` (if you're wrapping with multiple providers). This makes it clearer what the wrapper is doing.

## Summary

The key principles to remember:

1. **Use the right query** - Prefer `getByRole` as your first choice, use `findBy*` for async elements, and `queryBy*` only for checking non-existence
2. **Use proper assertions** - Use RNTL's built-in matchers (`toBeOnTheScreen()`, `toBeDisabled()`, etc.) instead of asserting on props directly
3. **Handle async operations correctly** - Always `await` `render()`, `renderHook`, `fireEvent`,and `userEvent` methods
4. **Use `waitFor` correctly** - Avoid side-effects in callbacks, use `findBy*` instead when possible, and keep callbacks focused
5. **Follow accessibility best practices** - Prefer ARIA attributes (`role`, `aria-label`) over `accessibility*` props
6. **Organize code well** - Use `screen` over destructuring, prefer `userEvent` over `fireEvent`, and don't use `cleanup()`

By following these principles, your tests will be more maintainable, accessible, and reliable.



---
url: /react-native-testing-library/docs/guides/llm-guidelines.md
---

# LLM Guidelines for React Native Testing Library

Actionable guidelines for writing tests with React Native Testing Library (RNTL) v14.

## Core APIs

### render

```tsx
const result = await render(<Component />, options?);
```

| Option    | Description                                                      |
| --------- | ---------------------------------------------------------------- |
| `wrapper` | React component to wrap the rendered component (e.g., providers) |

| Return                | Description                                      |
| --------------------- | ------------------------------------------------ |
| `rerender(component)` | Re-render with a new component (async)           |
| `unmount()`           | Unmount the rendered component (async)           |
| `toJSON()`            | Get JSON representation for snapshots            |
| `debug(options?)`     | Print the component tree to console              |
| `container`           | Root host element of the rendered tree           |
| `root`                | First child host element (your component's root) |

### screen

**Prefer `screen`** over destructuring from `render()`. Provides all query methods after `render()` is called.

```tsx
await render(<Component />);
screen.getByRole('button'); // Access queries via screen
```

### renderHook

```tsx
const { result, rerender, unmount } = await renderHook(() => useMyHook(), options?);
```

| Option         | Description                                        |
| -------------- | -------------------------------------------------- |
| `initialProps` | Initial props passed to the hook                   |
| `wrapper`      | React component to wrap the hook (e.g., providers) |

| Return             | Description                           |
| ------------------ | ------------------------------------- |
| `result.current`   | Current return value of the hook      |
| `rerender(props?)` | Re-render hook with new props (async) |
| `unmount()`        | Unmount the hook (async)              |

## Query Selection

- **Prefer `getByRole`** as first choice for querying elements
- **Query priority**: `getByRole` → `getByLabelText` → `getByPlaceholderText` → `getByText` → `getByDisplayValue` → `getByTestId` (last resort)
- **Use `findBy*`** for elements that appear asynchronously (after API calls, timeouts, state updates)
- **Use `queryBy*` ONLY** for checking non-existence (with `.not.toBeOnTheScreen()`)
- **Never use `getBy*`** for non-existence checks
- **Avoid `container.queryAll()`** - use `screen` queries instead
- **Query by visible text**, not `testID` when text is available

## Assertions

- **Use RNTL matchers** - prefer semantic matchers over prop assertions
- **Combine queries with matchers**: `expect(screen.getByText('Hello')).toBeOnTheScreen()`
- **No redundant null checks** - `getBy*` already throws if not found

## Jest Matchers Reference

| Matcher                           | Description                                                                                 |
| --------------------------------- | ------------------------------------------------------------------------------------------- |
| `toBeOnTheScreen()`               | Element is present in the element tree                                                      |
| `toBeVisible()`                   | Element is visible (checks style, `aria-hidden`, `accessibilityElementsHidden`, ancestors)  |
| `toBeEmptyElement()`              | Element has no children or text content                                                     |
| `toContainElement(element)`       | Element contains another element                                                            |
| `toBeEnabled()`                   | Element is not disabled (checks `aria-disabled`, `accessibilityState`, ancestors)           |
| `toBeDisabled()`                  | Element has `aria-disabled` or `accessibilityState={{ disabled: true }}` (checks ancestors) |
| `toBeBusy()`                      | Element has `aria-busy` or `accessibilityState={{ busy: true }}`                            |
| `toBeChecked()`                   | Element has `aria-checked` or `accessibilityState={{ checked: true }}`                      |
| `toBePartiallyChecked()`          | Element has `aria-checked="mixed"` or `accessibilityState={{ checked: 'mixed' }}`           |
| `toBeSelected()`                  | Element has `aria-selected` or `accessibilityState={{ selected: true }}`                    |
| `toBeExpanded()`                  | Element has `aria-expanded` or `accessibilityState={{ expanded: true }}`                    |
| `toBeCollapsed()`                 | Element has `aria-expanded={false}` or `accessibilityState={{ expanded: false }}`           |
| `toHaveTextContent(text)`         | Element has matching text content                                                           |
| `toHaveDisplayValue(value)`       | TextInput has matching display value                                                        |
| `toHaveAccessibleName(name?)`     | Element has matching `aria-label`, `accessibilityLabel`, or text content                    |
| `toHaveAccessibilityValue(value)` | Element has matching `aria-value*` or `accessibilityValue`                                  |
| `toHaveStyle(style)`              | Element has matching style                                                                  |
| `toHaveProp(name, value?)`        | Element has prop (use semantic matchers when possible)                                      |

## User Interactions

**Prefer `userEvent`** over `fireEvent` for realistic user interaction simulation. `userEvent` triggers the complete event sequence that real users would produce.

### userEvent (Preferred)

```tsx
const user = userEvent.setup();
```

| Method                               | Description                                                                         |
| ------------------------------------ | ----------------------------------------------------------------------------------- |
| `user.press(element)`                | Press an element (triggers `pressIn`, `pressOut`, `press`)                          |
| `user.longPress(element, options?)`  | Long press with optional `{ duration }`                                             |
| `user.type(element, text, options?)` | Type into TextInput (triggers `focus`, `keyPress`, `change`, `changeText` per char) |
| `user.clear(element)`                | Clear TextInput (select all + backspace)                                            |
| `user.paste(element, text)`          | Paste text into TextInput                                                           |
| `user.scrollTo(element, options)`    | Scroll a ScrollView with `{ y }` or `{ x }` offset                                  |

### fireEvent (Low-level)

Use only when `userEvent` doesn't support the event or when you need direct control.

| Method                                   | Description                                   |
| ---------------------------------------- | --------------------------------------------- |
| `fireEvent(element, eventName, ...data)` | Fire any event by name                        |
| `fireEvent.press(element)`               | Fire `onPress` only (no `pressIn`/`pressOut`) |
| `fireEvent.changeText(element, text)`    | Fire `onChangeText` directly                  |
| `fireEvent.scroll(element, eventData)`   | Fire `onScroll` with event data               |

## Async/Await (v14)

- **Always `await`**: `render()`, `fireEvent.*`, `renderHook()`, `userEvent.*`
- **Make test functions `async`**: `test('name', async () => { ... })`
- **Don't wrap in `act()`** - `render` and `fireEvent` handle it internally

## waitFor Usage

- **Use `findBy*`** instead of `waitFor` + `getBy*` when waiting for elements
- **Never perform side-effects** (like `fireEvent.press()`) inside `waitFor` callbacks
- **One assertion per `waitFor`** callback
- **Never pass empty callbacks** - always include a meaningful assertion
- **Place side-effects before `waitFor`** - perform actions, then wait for result

## Code Organization

- **Use `screen`** instead of destructuring from `render()`: `screen.getByText()` not `const { getByText } = render()`
- **Prefer `userEvent`** over `fireEvent` for realistic interactions
- **Don't use `cleanup()`** - handled automatically
- **Name wrappers descriptively**: `ThemeProvider` not `Wrapper`
- **Install ESLint plugin**: `eslint-plugin-testing-library`

## Quick Checklist

- ✅ Using `getByRole` as first choice?
- ✅ Using `await` for all async operations?
- ✅ Using `findBy*` for async elements (not `waitFor` + `getBy*`)?
- ✅ Using `queryBy*` only for non-existence?
- ✅ Using RNTL matchers (`toBeOnTheScreen()`, `toBeDisabled()`, etc.)?
- ✅ Using `screen` not destructuring from `render()`?
- ✅ Avoiding side-effects in `waitFor`?
- ✅ Using `userEvent` when appropriate?

## Example: Good Pattern

```tsx
import { render, screen } from '@testing-library/react-native';
import userEvent from '@testing-library/react-native';
import { Pressable, Text, TextInput, View } from 'react-native';

test('user can submit form', async () => {
  const user = userEvent.setup();

  const Component = () => {
    const [name, setName] = React.useState('');
    const [submitted, setSubmitted] = React.useState(false);

    return (
      <View>
        <TextInput role="textbox" aria-label="Name" value={name} onChangeText={setName} />
        <Pressable role="button" aria-label="Submit" onPress={() => setSubmitted(true)}>
          <Text>Submit</Text>
        </Pressable>
        {submitted && <Text role="alert">Form submitted!</Text>}
      </View>
    );
  };

  await render(<Component />);

  // ✅ getByRole as first choice
  const input = screen.getByRole('textbox', { name: 'Name' });
  const button = screen.getByRole('button', { name: 'Submit' });

  // ✅ userEvent for realistic interactions
  await user.type(input, 'John Doe');
  await user.press(button);

  // ✅ findBy* for async elements
  const successMessage = await screen.findByRole('alert');

  // ✅ RNTL matchers
  expect(successMessage).toBeOnTheScreen();
  expect(successMessage).toHaveTextContent('Form submitted!');
});
```

## Example: Anti-Patterns

```tsx
// ❌ Missing await
test('bad', () => {
  render(<Component />);
  fireEvent.press(screen.getByText('Submit'));
});

// ❌ getBy* for non-existence
expect(screen.getByText('Error')).not.toBeOnTheScreen();

// ❌ waitFor + getBy* instead of findBy*
await waitFor(() => {
  expect(screen.getByText('Loaded')).toBeOnTheScreen();
});

// ❌ Side-effect in waitFor
await waitFor(async () => {
  await fireEvent.press(button);
  expect(screen.getByText('Result')).toBeOnTheScreen();
});

// ❌ accessibility* props instead of ARIA
<Pressable accessibilityRole="button" accessibilityLabel="Submit" />;

// ❌ Destructuring from render
const { getByText } = await render(<Component />);
```

By following these guidelines, your tests will be more maintainable, accessible, and reliable.



---
url: /react-native-testing-library/docs/guides/troubleshooting.md
---

# Troubleshooting

This guide describes common issues found by users when integrating React Native Test Library to their projects:

## Example repository

We maintain an [example repository](https://github.com/callstack/react-native-testing-library/tree/main/examples/basic) with a React Native Testing Library setup using TypeScript.

If something doesn't work in your setup, check this repository for configuration examples.

## Undefined component error

> Warning: React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined.

This frequently happens when you mock a complex module incorrectly, e.g.:

```ts
jest.mock('@react-navigation/native', () => {
  return {
    useNavigation: jest.fn(),
  };
});
```

The above mock will mock `useNavigation` hook as intended, but at the same time all other exports from `@react-navigation/native` package are now `undefined`. If you want to use `NavigationContainer` component from the same package it will be `undefined` and result in the error above.

To mock only part of a package, re-export all other exports using `jest.requireActual`:

```ts
jest.mock('@react-navigation/native', () => {
  return {
    ...jest.requireActual('@react-navigation/native'),
    useNavigation: jest.fn(),
  };
});
```

That way the mock will re-export all of the `@react-navigation/native` members and overwrite only the `useNavigation` hook.

Alternatively, you can use `jest.spyOn` to mock package exports selectively.

### Mocking React Native

When mocking the `react-native` package, don't mock the whole package at once, as this has issues with `jest.requireActual`. Mock specific library paths inside the package instead, e.g.:

```ts title=jest-setup.ts
jest.mock('react-native/Libraries/EventEmitter/NativeEventEmitter');
```

## Act warnings

When writing tests you may encounter warnings connected with `act()` function. There are two kinds of these warnings:

- sync `act()` warning - `Warning: An update to Component inside a test was not wrapped in act(...)`
- async `act()` warning - `Warning: You called act(async () => ...) without await`

You can read more about `act()` function in our [understanding `act` function guide](/react-native-testing-library/docs/advanced/understanding-act.md).

Normally, you should not encounter sync `act()` warnings, but if that happens this probably indicate an issue with your test and should be investigated.

In case of async `act()` function this might happen more or less randomly, especially if your components contain async logic. So far this warning does not seem to affect test correctness.



---
url: /react-native-testing-library/docs/guides/faq.md
---

# FAQ

## Can I test the native features of React Native apps?

Short answer: no.

React Native Testing Library does not provide an entire React Native runtime since that would require running on a physical device
or iOS simulator/Android emulator to provision the underlying OS and platform APIs.

Instead of using React Native renderer, it simulates only the JavaScript part of its runtime
using [Test Renderer](https://github.com/mdjastrzebski/test-renderer) while providing queries
and event APIs ([User Event](/react-native-testing-library/docs/api/events/user-event.md), [Fire Event](/react-native-testing-library/docs/api/events/fire-event.md)) that mimicking certain behaviors from the actual runtime.

You can learn more about our testing environment [here](/react-native-testing-library/docs/advanced/testing-env.md).

This approach has benefits and limitations:

Benefits:

- Tests most of the logic of regular React Native apps
- Runs tests on any OS supported by Jest or other test runners, e.g., on CI
- Uses fewer resources than full runtime simulation
- Works with Jest fake timers

Limitations:

- Cannot test native features
- May not perfectly simulate certain JavaScript features, but we're working on it

The [User Event interactions](/react-native-testing-library/docs/api/events/user-event.md) solve some simulation issues by handling events more realistically than the basic [Fire Event API](/react-native-testing-library/docs/api/events/fire-event.md).

## Should I use/migrate to `screen` queries?

There is no need to migrate existing test code to use `screen`-bases queries. You can still use
queries and other functions returned by `render`. The `screen` object captures the latest `render` result.

For new code, use `screen`. [This article](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#not-using-screen) by Kent C. Dodds explains why.

## Should I use/migrate to User Event interactions?

Migrate existing tests to use the [User Event interactions](/react-native-testing-library/docs/api/events/user-event.md), which handle events more realistically than the basic [Fire Event API](/react-native-testing-library/docs/api/events/fire-event.md). This provides more confidence in your code quality.



---
url: /react-native-testing-library/docs/guides/community-resources.md
---

# Community resources

## Recommended content

- [The Testing Trophy and Testing Classifications](https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications) by Kent C. Dodds (2021) - classic article explaining testing philosophy behind all Testing Library implementations.
- [Common mistakes with React Testing Library](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library) by Kent C. Dodds (2020) - classic article explaining React Testing Library best practices, highly applicable to RNTL as well.
- [React Native — UI Testing (Ultimate Guide)](https://github.com/anisurrahman072/React-Native-Advanced-Guide/blob/master/Testing/RNTL-Component-Testing-ultimate-guide.md) by Anisur Rahman - comprehensive guide to RNTL testing
- [React Native Testing examples repo](https://github.com/vanGalilea/react-native-testing) by Steve Galili - extensive repo with RN testing examples for RNTL and Maestro

## Older, potentially outdated content

- [Where and how to start testing 🧪 your react-native app ⚛️ and how to keep on testin’](https://blog.usejournal.com/where-and-how-to-start-testing-your-react-native-app-%EF%B8%8F-and-how-to-keep-on-testin-ec3464fb9b41) by Steve Galili (2020) - article referencing Steve's examples repo.
- [Intro to React Native Testing Library & Jest Native](https://youtu.be/CpTQb0XWlRc) by Alireza Ghamkhar (2020) - video tutorial on RNTL setup and testing.



---
url: /react-native-testing-library/docs/advanced/testing-env.md
---

# Testing environment

:::info

This document is intended for a more advanced audience who want to understand the internals of our testing environment better, e.g., to contribute to the codebase. You should be able to write integration or component tests without reading this.

:::

React Native Testing Library lets you write integration and component tests for your React Native app or library. While the JSX code in tests closely resembles your React Native app, the underlying environment differs. This document describes the key elements of our testing environment and highlights things to be aware of when writing advanced tests or diagnosing issues.

## React renderers

React allows you to write declarative code using JSX, write function or class components, or use hooks like `useState`. You need to use a renderer to output the results of your components. Every React app uses some renderer:

- React Native is a renderer for mobile apps,
- React DOM is a renderer for web apps,
- There are other more [specialized renderers](https://github.com/chentsulin/awesome-react-renderer) that can e.g., render to console or HTML canvas.

When you run your tests in the React Native Testing Library, somewhat contrary to what the name suggests, they are actually **not** using React Native renderer. This is because this renderer needs to be run on an iOS or Android operating system, so it would need to run on a device or simulator.

## Test Renderer

Instead, RNTL uses [Test Renderer](https://github.com/mdjastrzebski/test-renderer), a modern, actively maintained renderer that renders to pure JavaScript objects without access to mobile OS and runs in a Node.js environment using Jest (or any other JavaScript test runner). Test Renderer replaces the deprecated `react-test-renderer` package and has better compatibility with React 19 and improved type safety.

Using Test Renderer has trade-offs:

Benefits:

- Tests run on most CIs (Linux, etc) without a mobile device or emulator
- Faster test execution
- Light runtime environment

Limitations:

- Tests don't execute native code
- Tests are unaware of view state managed by native components, e.g., focus, unmanaged text boxes, etc.
- Assertions don't operate on native view hierarchy
- Runtime behaviors are simulated, sometimes imperfectly

It's worth noting that the React Testing Library (web one) works a bit differently. While RTL also runs in Jest, it has access to a simulated browser DOM environment from the `jsdom` package, which allows it to use a regular React DOM renderer. Unfortunately, there is no similar React Native runtime environment package. This is probably because while the browser environment is well-defined and highly standardized, the React Native environment constantly evolves in sync with the evolution of underlying OS-es. Maintaining such an environment would require duplicating countless React Native behaviors and keeping them in sync as React Native develops.

## Element tree

Calling the `render()` function creates an element tree. This is done internally by invoking the `createRoot()` function from Test Renderer. The output tree represents your React Native component tree, containing only host elements. Each node of that tree corresponds to a host component that would have a counterpart in the native view hierarchy.

These tree elements are represented by `TestInstance` type from Test Renderer:

```tsx
interface TestInstance {
  type: ElementType;
  props: { [propName: string]: any };
  parent: TestInstance | null;
  children: Array<TestInstance | string>;

  // Other props and methods
}
```

For more details, see the [Test Renderer documentation](https://github.com/mdjastrzebski/test-renderer).

## Host and composite components

To understand RNTL's element tree, it's important to know the difference between host and composite components in React Native:

- [Host components](https://reactnative.dev/architecture/glossary#react-host-components-or-host-components) have direct counterparts in the native view tree. Typical examples are `<View>`, `<Text>`, `<TextInput>`, and `<Image>` from React Native. You can think of these as an analog of `<div>`, `<span>` etc on the Web. You can also create custom host views as native modules or import them from 3rd party libraries, like React Navigation or React Native Gesture Handler.
- [Composite components](https://reactnative.dev/architecture/glossary#react-composite-components) are React code organization units that exist only on the JavaScript side of your app. Typical examples are components you create (function and class components), components imported from React Native (`View`, `Text`, etc.), or 3rd party packages.

That might initially sound confusing since we put React Native's `View` in both categories. There are two `View` components: composite and host. The relation between them is as follows:

- Composite `View` is the type imported from the `react-native` package. It is a JavaScript component that renders the host `View` as its only child.
- Host `View`, which you do not render directly. React Native takes the props you pass to the composite `View`, does some processing on them and passes them to the host `View`.

In a full React tree, this would look like:

```jsx
* <View> (composite)
  * <View> (host)
    * children prop passed in JSX
```

A similar relation exists between other composite and host pairs: e.g. `Text`, `TextInput`, and `Image` components.

Not all React Native components are organized this way, e.g., when you use `Pressable` (or `TouchableOpacity`), there is no host `Pressable`, but composite `Pressable` is rendering a host `View` with specific props being set:

```jsx
* <Pressable> (composite)
  * <View accessible={true} {...}> (host)
    * children prop passed in JSX
```

### Host-only element tree

In RNTL v14, [Test Renderer](https://github.com/mdjastrzebski/test-renderer) only exposes host elements in the element tree. Composite components aren't visible in the tree—you only see their host element output. This aligns with Testing Library's philosophy: tests should focus on what users can see and interact with (host elements), not implementation details (composite components).

For a `TestInstance`, the `type` prop is always a string value representing the host component name, e.g., `"View"`, `"Text"`, `"TextInput"`.

## Tree nodes

RNTL v14 queries and the element tree only expose host elements. Tests assert on what users can see and interact with. Host elements represent the actual UI controls users interact with, while composite components exist purely in the JavaScript domain.

### Understanding props

When asserting props on host elements, you're verifying what actually reaches the native view. This is important because composite components may process, transform, or even forget to pass props to their host children.

```jsx
function ForgotToPassPropsButton({ title, onPress, style }) {
  return (
    <Pressable>
      <Text>{title}</Text>
    </Pressable>
  );
}
```

In the above example, the component accepts `onPress` and `style` props but doesn't pass them to host views, so they won't affect the user interface. By testing host elements, RNTL helps you catch these issues: if a prop doesn't reach a host element, users won't see or interact with it.

## Tree navigation

:::caution
You should avoid navigating over the element tree, as this makes your testing code fragile and may result in false positives. This section is more relevant for people who want to contribute to our codebase.
:::

You can navigate the tree of host elements using `parent` or `children` props of a `TestInstance`. Be careful when doing this, as the tree structure for third-party components can change independently from your code and cause unexpected test failures.

## Queries

All Testing Library queries return host components to encourage the best practices described above. Since v14, RNTL uses [Test Renderer](https://github.com/mdjastrzebski/test-renderer), which only renders host elements, making it impossible to query composite components directly.



---
url: /react-native-testing-library/docs/advanced/third-party-integration.md
---

# Third-Party Library Integration

The React Native Testing Library is designed to simulate the core behaviors of React Native. However, it does not replicate the internal logic of third-party libraries. This guide explains how to integrate your library with RNTL.

## Handling Events in Third-Party Libraries

RNTL provides two subsystems to simulate events:

- **Fire Event**: A lightweight simulation system that can trigger event handlers defined on both host and composite components.
- **User Event**: A more realistic interaction simulation system that can trigger event handlers defined only on host components.

In many third-party libraries, event handling involves native code, which means RNTL cannot fully simulate the event flow, as it runs only JavaScript code. To address this limitation, you can use `testOnly_on*` props on host components to expose custom events to RNTL’s event subsystems. Both subsystems will first attempt to locate the standard `on*` event handlers; if these are not available, they fall back to the `testOnly_on*` handlers.

### Example: React Native Gesture Handler

React Native Gesture Handler (RNGH) provides a composite [Pressable](https://docs.swmansion.com/react-native-gesture-handler/docs/components/pressable/) component with `onPress*` props. These event handlers are not exposed on the rendered host views; instead, they are invoked via RNGH’s internal event flow, which involves native modules. As a result, they are not accessible to RNTL’s event subsystems.

To enable RNTL to interact with RNGH’s `Pressable` component, the library exposes `testOnly_onPress*` props on the `NativeButton` host component rendered by `Pressable`. This adjustment allows RNTL to simulate interactions during testing.

```tsx title="Simplified RNGH Pressable component"
function Pressable({ onPress, onPressIn, onPressOut, onLongPress, ... }) {

  // Component logic...

  const isTestEnv = process.env.NODE_ENV === 'test';

  return (
    <GestureDetector gesture={gesture}>
      <NativeButton
        /* Other props... */
        testOnly_onPress={isTestEnv ? onPress : undefined}
        testOnly_onPressIn={isTestEnv ? onPressIn : undefined}
        testOnly_onPressOut={isTestEnv ? onPressOut : undefined}
        testOnly_onLongPress={isTestEnv ? onLongPress : undefined}
      />
    </GestureDetector>
  );
}
```



---
url: /react-native-testing-library/docs/advanced/understanding-act.md
---

# Understanding `act` function

When writing RNTL tests, cryptic [`act()`](https://react.dev/link/wrap-tests-with-act) function errors logged to console often confuse developers. This article explains the purpose and behavior of `act()` so you can write tests with more confidence.

## `act` warning

Let's start with a typical `act()` warning logged to console:

```
An update to Root inside a test was not wrapped in act(...).

When testing, code that causes React state updates should be wrapped into act(...):

act(() => {
  /* fire events that update state */
});
/* assert on the output */

This ensures that you're testing the behavior the user would see in the browser.
Learn more at https://react.dev/link/wrap-tests-with-act
```

## Understanding `act`

### Responsibility

This function is intended only for using in automated tests and works only in development mode. Attempting to use it in production build will throw an error.

The responsibility for `act` function is to make React renders and updates work in tests in a similar way they work in real application by grouping and executing related units of interaction (e.g. renders, effects, etc) together.

Let's demonstrate this with a small experiment. First, define a function component that uses `useEffect`:

```jsx
function TestComponent() {
  const [count, setCount] = React.useState(0);
  React.useEffect(() => {
    setCount((c) => c + 1);
  }, []);

  return <Text>Count {count}</Text>;
}
```

In the following tests we will directly use [Test Renderer](https://github.com/mdjastrzebski/test-renderer) instead of RNTL `render` function to render our component for tests. In order to expose familiar queries like `getByText` we will use `within` function from RNTL.

```jsx
import { createRoot } from 'test-renderer';
import { within } from '@testing-library/react-native';

test('render without act', () => {
  const renderer = createRoot();
  renderer.render(<TestComponent />);

  // Bind RNTL queries for root element.
  const view = within(renderer.container);
  expect(view.getByText('Count 0')).toBeTruthy();
});
```

When testing without `act` wrapping the render call, the assertion runs just after rendering but before `useEffect` effects are applied. This isn't what we expected.

```jsx
import { createRoot } from 'test-renderer';
import { act, within } from '@testing-library/react-native';

test('render with act', async () => {
  const renderer = createRoot();
  await act(() => {
    renderer.render(<TestComponent />);
  });

  // Bind RNTL queries for root element.
  const view = within(renderer.container);
  expect(view.getByText('Count 1')).toBeTruthy();
});
```

**Note**: In v14, `act` is now async by default and always returns a Promise. You should always use `await act(...)`.

When wrapping rendering call with `act` we see that the changes caused by `useEffect` hook have been applied as we would expect.

### When to use act

The name `act` comes from [Arrange-Act-Assert](http://wiki.c2.com/?ArrangeActAssert) unit testing pattern. Which means it's related to part of the test when we execute some actions on the component tree.

So far we learned that `act` function allows tests to wait for all pending React interactions to be applied before we make our assertions. When using `act` we get guarantee that any state updates will be executed as well as any enqueued effects will be executed.

Therefore, we should use `act` whenever there is some action that causes element tree to render, particularly:

- initial render call - `renderer.render` call
- re-rendering of component - `renderer.render` call with updated element
- triggering any event handlers that cause component tree render

Thankfully, for these basic cases RNTL has got you covered as our `render`, `rerender` and `fireEvent` methods already wrap their calls in `act` so that you do not have to do it explicitly. In v14, these functions are all async and should be awaited.

Note that `act` calls can be safely nested and internally form a stack of calls.

### Implementation

The `act` implementation is defined in the [ReactAct.js source file](https://github.com/facebook/react/blob/main/packages/react/src/ReactAct.js) inside React repository. RNTL v14 requires React 19+, which provides the `act` function directly via `React.act`.

RNTL exports `act` for convenience as defined in the [act.ts source file](https://github.com/callstack/react-native-testing-library/blob/main/src/act.ts). In v14, `act` is async by default and always returns a Promise. This works with async React features like `Suspense` boundaries and the `use()` hook. The underlying implementation wraps React's `act` function to ensure consistent async behavior.

**Important**: You should always use `act` exported from `@testing-library/react-native` rather than the one from `react`. The RNTL version automatically ensures async behavior, whereas using `React.act` directly could still trigger synchronous act behavior if used improperly, leading to subtle test issues.

## Asynchronous code

In v14, `act` is always async and returns a Promise. While the callback you pass to `act` can be synchronous (dealing with things like synchronous effects or mocks using already resolved promises), the `act` function itself should always be awaited. However, not all component code is synchronous. Frequently our components or mocks contain some asynchronous behaviours like `setTimeout` calls or network calls.

### Handling asynchronous operations

When the callback passed to `act` contains asynchronous operations, the Promise returned by `act` will resolve only after those operations complete.

Here's a simple example with a component using `setTimeout` to simulate asynchronous behavior:

```jsx
function TestAsyncComponent() {
  const [count, setCount] = React.useState(0);
  React.useEffect(() => {
    setTimeout(() => {
      setCount((c) => c + 1);
    }, 50);
  }, []);

  return <Text>Count {count}</Text>;
}
```

```jsx
import { render, screen } from '@testing-library/react-native';

test('render async natively', async () => {
  await render(<TestAsyncComponent />);
  expect(screen.getByText('Count 0')).toBeOnTheScreen();
});
```

If we test our component in a native way without handling its asynchronous behaviour we will end up with an act warning. This is because the `setTimeout` callback will trigger a state update after the test has finished.

### Solution with fake timers

Use Jest's fake timers:

```jsx
test('render with fake timers', async () => {
  jest.useFakeTimers();
  await render(<TestAsyncComponent />);

  await act(() => {
    jest.runAllTimers();
  });
  expect(screen.getByText('Count 1')).toBeOnTheScreen();
});
```

**Note**: In v14, both `render` and `act` are async by default, so you should await them.

That way we can wrap `jest.runAllTimers()` call which triggers the `setTimeout` updates inside an `act` call, hence resolving the act warning.

### Solution with real timers

With real timers, things get more complex. Start with a simple solution: wrap an async `act()` call for the expected duration of component updates:

```jsx
test('render with real timers - sleep', async () => {
  await render(<TestAsyncComponent />);
  await act(() => {
    await sleep(100); // Wait a bit longer than setTimeout in `TestAsyncComponent`
  });

  expect(screen.getByText('Count 1')).toBeOnTheScreen();
});
```

This works correctly as we use an explicit async `act()` call that resolves the console error. However, it relies on our knowledge of exact implementation details which is a bad practice.

A better solution uses `waitFor` to wait for the desired state:

```jsx
test('render with real timers - waitFor', async () => {
  await render(<TestAsyncComponent />);

  await waitFor(() => screen.getByText('Count 1'));
  expect(screen.getByText('Count 1')).toBeOnTheScreen();
});
```

This also works correctly, because `waitFor` call executes async `act()` call internally.

The above code can be simplified using `findBy` query:

```jsx
test('render with real timers - findBy', async () => {
  await render(<TestAsyncComponent />);

  expect(await screen.findByText('Count 1')).toBeOnTheScreen();
});
```

This also works since `findByText` internally calls `waitFor` which uses async `act()`.

Note that all of the above examples are async tests using & awaiting async `act()` function call.

## References

- [React `act` implementation source](https://github.com/facebook/react/blob/main/packages/react/src/ReactAct.js)
- [React testing documentation](https://react.dev/link/wrap-tests-with-act)



---
url: /react-native-testing-library/docs/api.md
---

# API Overview

React Native Testing Library consists of following APIs:

- [`render` function](/react-native-testing-library/docs/api/render.md) - render your UI components for testing purposes
- [`screen` object](/react-native-testing-library/docs/api/screen.md) - access rendered UI:
  - [Queries](/react-native-testing-library/docs/api/queries.md) - find relevant components by various predicates: role, text, test ids, etc
  - Lifecycle methods: [`rerender`](/react-native-testing-library/docs/api/screen.md#rerender), [`unmount`](/react-native-testing-library/docs/api/screen.md#unmount)
  - Helpers: [`debug`](/react-native-testing-library/docs/api/screen.md#debug), [`toJSON`](/react-native-testing-library/docs/api/screen.md#tojson), [`root`](/react-native-testing-library/docs/api/screen.md#root)
- [Jest matchers](/react-native-testing-library/docs/api/jest-matchers.md) - validate assumptions about your UI
- [User Event](/react-native-testing-library/docs/api/events/user-event.md) - simulate common user interactions like [`press`](/react-native-testing-library/docs/api/events/user-event.md#press) or [`type`](/react-native-testing-library/docs/api/events/user-event.md#type) in a realistic way
- [Fire Event](/react-native-testing-library/docs/api/events/fire-event.md) - simulate any component event in a simplified way purposes
- Misc APIs:
  - [`renderHook` function](/react-native-testing-library/docs/api/misc/render-hook.md) - render hooks for testing
  - [Async utils](/react-native-testing-library/docs/api/misc/async.md): `findBy*` queries, `waitFor`, `waitForElementToBeRemoved`
  - [Configuration](/react-native-testing-library/docs/api/misc/config.md): `configure`, `resetToDefaults`
  - [Accessibility](/react-native-testing-library/docs/api/misc/accessibility.md): accessible name, `isHiddenFromAccessibility`
  - [Other](/react-native-testing-library/docs/api/misc/other.md): `within`, `act`, `cleanup`



---
url: /react-native-testing-library/cookbook/index.md
---

# Introduction

Welcome to the **React Native Testing Library (RNTL) Cookbook**!
This app is your go-to resource for learning how to effectively test React Native applications.
It provides a collection of **best practices**, **ready-made recipes**, and **tips & tricks** to
simplify and improve your testing workflow. Whether you’re a beginner just getting started or a
seasoned developer looking to sharpen your
skills, the Cookbook has something for everyone.

## What's Inside the Cookbook?

The Cookbook is currently organized into **three main chapters**:

- **Basic Recipes**: A great starting point, covering essential testing scenarios such as async
  operations and custom render functions.
- **Advanced Recipes**: More complex scenarios like network requests and in the future, navigation
  testing and more.
- **State Management Recipes**: Best practices for testing state management libraries

Each recipe includes a clear explanation along with a corresponding code example to help you get
hands-on with testing. Checkout
the [Cookbook App](https://github.com/callstack/react-native-testing-library/tree/main/examples/cookbook#rntl-cookbook) to see the
recipes in action.

## What's Next?

Join the conversation
on [GitHub](https://github.com/callstack/react-native-testing-library/issues/1624) here to discuss
ideas, ask questions, or provide feedback.



---
url: /react-native-testing-library/cookbook/basics/async-events.md
---

# Async Events

## Summary

In RNTL v14, all tests are async since `render()`, `fireEvent()`, and other core APIs return Promises. Beyond the basic async APIs, there are additional async utilities for handling events that complete over time:

1. **Waiting for elements to appear**: Use `findBy*` queries when elements appear after some delay (e.g., after data fetching).
2. **Waiting for conditions**: Use `waitFor()` to wait for arbitrary conditions to be met.
3. **Waiting for elements to disappear**: Use `waitForElementToBeRemoved()` when elements should be removed after some action.

These utilities help you write reliable tests that properly handle timing in your application.

### Example

Consider a test for a user signing in with correct credentials:

```javascript
test('User can sign in with correct credentials', async () => {
  // Typical test setup
  const user = userEvent.setup();
  await render(<App />);

  // No need to use async here, components are already rendered
  expect(screen.getByRole('header', { name: 'Sign in to Hello World App!' })).toBeOnTheScreen();

  // Using await as User Event requires it
  await user.type(screen.getByLabelText('Username'), 'admin');
  await user.type(screen.getByLabelText('Password'), 'admin1');
  await user.press(screen.getByRole('button', { name: 'Sign In' }));

  // Using await as sign in operation is asynchronous
  expect(await screen.findByRole('header', { name: 'Welcome admin!' })).toBeOnTheScreen();

  // Follow-up assertions do not need to be async, as we already waited for sign in operation to complete
  expect(
    screen.queryByRole('header', { name: 'Sign in to Hello World App' }),
  ).not.toBeOnTheScreen();
  expect(screen.queryByLabelText('Username')).not.toBeOnTheScreen();
  expect(screen.queryByLabelText('Password')).not.toBeOnTheScreen();
});
```

## Async utilities

There are several asynchronous utilities you might use in your tests.

### `findBy*` queries

The most common are the [`findBy*` queries](/react-native-testing-library/docs/api/queries.md#find-by). These are useful when waiting for a matching element to appear. They can be understood as a [`getBy*` queries](/react-native-testing-library/docs/api/queries.md#get-by) used in conjunction with a [`waitFor` function](/react-native-testing-library/docs/api/misc/async.md#waitfor).

They accept the same predicates as `getBy*` queries like `findByRole`, `findByTest`, etc. They also have a multiple elements variant called [`findAllBy*`](/react-native-testing-library/docs/api/queries.md#find-all-by).

```typescript
function findByRole: (
  role: TextMatch,
  queryOptions?: {
    // Query specific options
  }
  waitForOptions?: {
    timeout?: number;
    interval?: number;
    // ..
  }
): Promise<TestInstance>;
```

Each query has a default `timeout` value of 1000 ms and a default `interval` of 50 ms. Custom timeout and check intervals can be specified if needed, as shown below:

#### Example

```typescript
const button = await screen.findByRole(
  'button',
  { name: 'Start' },
  { timeout: 1000, interval: 50 },
);
```

Alternatively, a default global `timeout` value can be set using the [`configure` function](/react-native-testing-library/docs/api/misc/config.md#configure):

```typescript
configure({ asyncUtilTimeout: timeout });
```

### `waitFor` function

The `waitFor` function is another option, serving as a lower-level utility in more advanced cases.

```typescript
function waitFor<T>(
  expectation: () => T,
  options?: {
    timeout: number;
    interval: number;
  },
): Promise<T>;
```

It accepts an `expectation` to be validated and repeats the check every defined interval until it no longer throws an error. Similarly to `findBy*` queries they accept `timeout` and `interval` options and have the same default values of 1000ms for timeout, and a checking interval of 50 ms.

#### Example

```typescript
await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1));
```

If you want to use it with `getBy*` queries, use the `findBy*` queries instead, as they essentially do the same, but offer better developer experience.

### `waitForElementToBeRemoved` function

A specialized function, [`waitForElementToBeRemoved`](/react-native-testing-library/docs/api/misc/async.md#waitforelementtoberemoved), is used to verify that a matching element was present but has since been removed.

```typescript
function waitForElementToBeRemoved<T>(
  expectation: () => T,
  options?: {
    timeout: number;
    interval: number;
  },
): Promise<T> {}
```

This function is, in a way, the negation of `waitFor` as it expects the initial expectation to be true (not throw an error), only to turn invalid (start throwing errors) on subsequent runs. It operates using the same `timeout` and `interval` parameters as `findBy*` queries and `waitFor`.

#### Example

```typescript
await waitForElementToBeRemoved(() => getByText('Hello World'));
```

## Fake Timers

Asynchronous tests can take long to execute due to the delays introduced by asynchronous operations. To mitigate this, fake timers can be used. These are particularly useful when delays are mere waits, such as the 130 milliseconds wait introduced by the UserEvent `press()` event due to React Native runtime behavior or simulated 1000 wait in a API call mock. Fake timers allow for precise fast-forwarding through these wait periods.

Here are the basics of using [Jest fake timers](https://jestjs.io/docs/timer-mocks):

- Enable fake timers with: `jest.useFakeTimers()`
- Disable fake timers with: `jest.useRealTimers()`
- Advance fake timers forward with: `jest.advanceTimersByTime(interval)`
- Run **all timers** to completion with: `jest.runAllTimers()`
- Run **currently pending timers** to completion with: `jest.runOnlyPendingTimers()`

Be cautious when running all timers to completion as it might create an infinite loop if these timers schedule follow-up timers. In such cases, it's safer to use `jest.runOnlyPendingTimers()` to avoid ending up in an infinite loop of scheduled tasks.

You can use both built-in Jest fake timers, as well as [Sinon.JS fake timers](https://sinonjs.org/releases/latest/fake-timers/).

Note: you do not need to advance timers by hand when using User Event API, as it's automatically.



---
url: /react-native-testing-library/cookbook/basics/custom-render.md
---

# Custom `render` function

### Summary

RNTL exposes the `render` function as the primary entry point for tests. If you make complex, repeating setups for your tests, consider creating a custom render function. The idea is to encapsulate common setup steps and test wiring inside a render function suitable for your tests.

### Example

```tsx title=test-utils.ts
// ...

interface RenderWithProvidersProps {
  user?: User | null;
  theme?: Theme;
}

export async function renderWithProviders<T>(
  ui: React.ReactElement<T>,
  options?: RenderWithProvidersProps,
) {
  return await render(
    <UserProvider.Provider value={options?.user ?? null}>
      <ThemeProvider.Provider value={options?.theme ?? 'light'}>{ui}</ThemeProvider.Provider>
    </UserProvider.Provider>,
  );
}
```

```tsx title=custom-render/index.test.tsx
import { screen } from '@testing-library/react-native';
import { renderWithProviders } from '../test-utils';
// ...

test('renders WelcomeScreen with user', async () => {
  await renderWithProviders(<WelcomeScreen />, { user: { name: 'Jar-Jar' } });
  expect(screen.getByText(/hello Jar-Jar/i)).toBeOnTheScreen();
});

test('renders WelcomeScreen without user', async () => {
  await renderWithProviders(<WelcomeScreen />, { user: null });
  expect(screen.getByText(/hello stranger/i)).toBeOnTheScreen();
});
```

Example [full source code](https://github.com/callstack/react-native-testing-library/tree/main/examples/cookbook/custom-render).

### More info

#### Additional params

A custom render function might accept additional parameters to allow for setting up different start conditions for a test, e.g., the initial state for global state management.

```tsx title=SomeScreen.test.tsx
test('renders SomeScreen for logged in user', async () => {
  await renderScreen(<SomeScreen />, { state: loggedInState });
  // ...
});
```

#### Multiple functions

Depending on the situation, you may declare more than one custom render function. For example, you have one function for testing application flows and a second for testing individual screens.

```tsx title=test-utils.tsx
function renderNavigator(ui, options);
function renderScreen(ui, options);
```

#### Async setup

Since `render` is async, your custom render function should be marked as `async` and use `await render()`. This pattern also makes it easy to add additional async setup if needed:

```tsx title=SomeScreen.test.tsx
async function renderWithData<T>(ui: React.ReactElement<T>) {
  const data = await fetchTestData();
  return await render(<DataProvider value={data}>{ui}</DataProvider>);
}

test('renders SomeScreen', async () => {
  await renderWithData(<SomeScreen />);
  // ...
});
```



---
url: /react-native-testing-library/cookbook/advanced/network-requests.md
---

# Network Requests

## Introduction

Mocking network requests is an essential part of testing React Native applications. By mocking
network
requests, you can control the data that is returned from the server and test how your application
behaves in different scenarios, such as when the request is successful or when it fails.

In this guide, we will show you how to mock network requests and guard your test suits from unwanted
and unmocked/unhandled network requests

:::info
To simulate a real-world scenario, we will use the [Random User Generator API](https://randomuser.me/) that provides random user data.
:::

## Phonebook Example

Let's assume we have a simple phonebook application that
uses [`fetch`](https://reactnative.dev/docs/network#using-fetch) for fetching Data from a server.
In our case, we have a list of contacts and favorites that we want to display in our application.

This is how the root of the application looks like:

```tsx title=network-requests/Phonebook.tsx
import React, { useEffect, useState } from 'react';
import { Text } from 'react-native';
import { User } from './types';
import ContactsList from './components/ContactsList';
import FavoritesList from './components/FavoritesList';
import getAllContacts from './api/getAllContacts';
import getAllFavorites from './api/getAllFavorites';

export default () => {
  const [usersData, setUsersData] = useState<User[]>([]);
  const [favoritesData, setFavoritesData] = useState<User[]>([]);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const _getAllContacts = async () => {
      const _data = await getAllContacts();
      setUsersData(_data);
    };
    const _getAllFavorites = async () => {
      const _data = await getAllFavorites();
      setFavoritesData(_data);
    };

    const run = async () => {
      try {
        await Promise.all([_getAllContacts(), _getAllFavorites()]);
      } catch (e) {
        const message = isErrorWithMessage(e) ? e.message : 'Something went wrong';
        setError(message);
      }
    };

    void run();
  }, []);

  if (error) {
    return <Text>An error occurred: {error}</Text>;
  }

  return (
    <>
      <FavoritesList users={favoritesData} />
      <ContactsList users={usersData} />
    </>
  );
};
```

We fetch the contacts from the server using the `getAllFavorites` function that utilizes `fetch`.

```tsx title=network-requests/api/getAllContacts.ts
import { User } from '../types';

export default async (): Promise<User[]> => {
  const res = await fetch('https://randomuser.me/api/?results=25');
  if (!res.ok) {
    throw new Error(`Error fetching contacts`);
  }
  const json = await res.json();
  return json.results;
};
```

We have similar function for fetching the favorites, but this time limiting the results to 10.

```tsx title=network-requests/api/getAllFavorites.ts
import { User } from '../types';

export default async (): Promise<User[]> => {
  const res = await fetch('https://randomuser.me/api/?results=10');
  if (!res.ok) {
    throw new Error(`Error fetching favorites`);
  }
  const json = await res.json();
  return json.results;
};
```

Our `FavoritesList` component is a simple component that displays the list of favorite contacts and
their avatars horizontally.

```tsx title=network-requests/components/FavoritesList.tsx
import {FlatList, Image, StyleSheet, Text, View} from 'react-native';
import React, {useCallback} from 'react';
import type {ListRenderItem} from '@react-native/virtualized-lists';
import {User} from '../types';

export default ({users}: { users: User[] }) => {
  const renderItem: ListRenderItem<User> = useCallback(({item: {picture}}) => {
    return (
      <View style={styles.userContainer}>
        <Image
          source={{uri: picture.thumbnail}}
          style={styles.userImage}
          accessibilityLabel={'favorite-contact-avatar'}
        />
      </View>
    );
  }, []);

  if (users.length === 0) return (
    <View style={styles.loaderContainer}>
      <Text>Figuring out your favorites...</Text>
    </View>
  );

  return (
    <View style={styles.outerContainer}>
      <Text>⭐My Favorites</Text>
      <FlatList<User>
        horizontal
        showsHorizontalScrollIndicator={false}
        data={users}
        renderItem={renderItem}
        keyExtractor={(item, index) => `${index}-${item.id.value}`}
      />
    </View>
  );
};

// Looking for styles?
// Check examples/cookbook/app/advanced/components/FavoritesList.tsx
const styles =
...
```

Our `ContactsList` component is similar to the `FavoritesList` component, but it displays the list
of
all contacts vertically.

```tsx title=network-requests/components/ContactsList.tsx
import { FlatList, Image, StyleSheet, Text, View } from 'react-native';
import React, { useCallback } from 'react';
import type { ListRenderItem } from '@react-native/virtualized-lists';
import { User } from '../types';

export default ({ users }: { users: User[] }) => {
  const renderItem: ListRenderItem<User> = useCallback(
    ({ item: { name, email, picture, cell }, index }) => {
      const { title, first, last } = name;
      const backgroundColor = index % 2 === 0 ? '#f9f9f9' : '#fff';
      return (
        <View style={[{ backgroundColor }, styles.userContainer]}>
          <Image source={{ uri: picture.thumbnail }} style={styles.userImage} />
          <View>
            <Text>
              Name: {title} {first} {last}
            </Text>
            <Text>Email: {email}</Text>
            <Text>Mobile: {cell}</Text>
          </View>
        </View>
      );
    },
    [],
  );

  if (users.length === 0) return <FullScreenLoader />;

  return (
    <View>
      <FlatList<User>
        data={users}
        renderItem={renderItem}
        keyExtractor={(item, index) => `${index}-${item.id.value}`}
      />
    </View>
  );
};

// Looking for styles or FullScreenLoader component?
// Check examples/cookbook/app/advanced/components/ContactsList.tsx
const FullScreenLoader = () => ...
const styles = ...
```

## Start testing with a simple test

In our initial test we would like to test if the `PhoneBook` component renders the `FavoritesList`
and `ContactsList` components correctly.
We will need to mock the network requests and their corresponding responses to ensure that the component behaves as
expected. To mock the network requests we will use [MSW (Mock Service Worker)](https://mswjs.io/docs/getting-started).

:::note
We recommend using the Mock Service Worker (MSW) library to declaratively mock API communication in your tests instead of stubbing `fetch`, or relying on third-party adapters.
:::

:::info
You can install MSW by running `npm install msw --save-dev` or `yarn add msw --dev`.
More info regarding installation can be found in [MSW's getting started guide](https://mswjs.io/docs/getting-started#step-1-install).

Please make sure you're also aware of [MSW's setup guide](https://mswjs.io/docs/integrations/react-native).
Please be minded that the MSW's setup guide is potentially incomplete and might contain discrepancies/missing pieces.
:::

```tsx title=network-requests/Phonebook.test.tsx
import { render, screen, waitForElementToBeRemoved } from '@testing-library/react-native';
import React from 'react';
import PhoneBook from '../PhoneBook';
import { User } from '../types';
import {http, HttpResponse} from "msw";
import {setupServer} from "msw/node";

// Define request handlers and response resolvers for random user API.
// By default, we always return the happy path response.
const handlers = [
  http.get('https://randomuser.me/api/*', () => {
    return HttpResponse.json(DATA);
  }),
];

// Setup a request interception server with the given request handlers.
const server = setupServer(...handlers);

// Enable API mocking via Mock Service Worker (MSW)
beforeAll(() => server.listen());
// Reset any runtime request handlers we may add during the tests
afterEach(() => server.resetHandlers());
// Disable API mocking after the tests are done
afterAll(() => server.close());

describe('PhoneBook', () => {
  it('fetches all contacts and favorites successfully and renders lists in sections correctly', async () => {
    await render(<PhoneBook />);

    await waitForElementToBeRemoved(() => screen.getByText(/users data not quite there yet/i));
    expect(await screen.findByText('Name: Mrs Ida Kristensen')).toBeOnTheScreen();
    expect(await screen.findByText('Email: ida.kristensen@example.com')).toBeOnTheScreen();
    expect(await screen.findAllByText(/name/i)).toHaveLength(3);
    expect(await screen.findByText(/my favorites/i)).toBeOnTheScreen();
    expect(await screen.findAllByLabelText('favorite-contact-avatar')).toHaveLength(3);
  });
});

const DATA: { results: User[] } = {
  results: [
    {
      name: {
        title: 'Mrs',
        first: 'Ida',
        last: 'Kristensen',
      },
      email: 'ida.kristensen@example.com',
      id: {
        name: 'CPR',
        value: '250562-5730',
      },
      picture: {
        large: 'https://randomuser.me/api/portraits/women/26.jpg',
        medium: 'https://randomuser.me/api/portraits/med/women/26.jpg',
        thumbnail: 'https://randomuser.me/api/portraits/thumb/women/26.jpg',
      },
      cell: '123-4567-890',
    },
    // For brevity, we have omitted the rest of the users, you can still find them in
    // examples/cookbook/app/network-requests/__tests__/test-utils.ts
    ...
  ],
};
```

:::info
More info regarding how to describe the network using request handlers, intercepting a request and handling its response can be found in the [MSW's documentation](https://mswjs.io/docs/getting-started#step-2-describe).
:::

## Testing error handling

As we are dealing with network requests, and things can go wrong, we should also cover the case when
the API request fails. In this case, we would like to test how our application behaves when the API request fails.

:::info
The nature of the network can be highly dynamic, which makes it challenging to describe it completely in a fixed list of request handlers.
MSW provides us the means to override any particular network behavior using the designated `.use()` API.
More info can be found in [MSW's Network behavior overrides documentation](https://mswjs.io/docs/best-practices/network-behavior-overrides)
:::

```tsx title=network-requests/Phonebook.test.tsx
...

const mockServerFailureForGetAllContacts = () => {
  server.use(
    http.get('https://randomuser.me/api/', ({ request }) => {
      // Construct a URL instance out of the intercepted request.
      const url = new URL(request.url);
      // Read the "results" URL query parameter using the "URLSearchParams" API.
      const resultsLength = url.searchParams.get('results');
      // Simulate a server error for the get all contacts request.
      // We check if the "results" query parameter is set to "25"
      // to know it's the correct request to mock, in our case get all contacts.
      if (resultsLength === '25') {
        return new HttpResponse(null, { status: 500 });
      }
      // Return the default response for all other requests that match URL and verb. (in our case get favorites)
      return HttpResponse.json(DATA);
    }),
  );
};

describe('PhoneBook', () => {
...
  it('fails to fetch all contacts and renders error message', async () => {
    mockServerFailureForGetAllContacts();
    await render(<PhoneBook />);

    await waitForElementToBeRemoved(() => screen.getByText(/users data not quite there yet/i));
    expect(
      await screen.findByText(/an error occurred: error fetching contacts/i),
    ).toBeOnTheScreen();
  });
});

```

## Global guarding against unwanted API requests

As mistakes may happen, we might forget to mock a network request in one of our tests in the future.
To prevent us from happening, and alert when a certain network request is left unhandled, you may choose to
move MSW's server management from `PhoneBook.test.tsx` to Jest's setup file via [`setupFilesAfterEnv`](https://jestjs.io/docs/configuration#setupfilesafterenv-array).

```tsx title=examples/cookbook/jest-setup.ts
// Enable API mocking via Mock Service Worker (MSW)
beforeAll(() => server.listen());
// Reset any runtime request handlers we may add during the tests
afterEach(() => server.resetHandlers());
// Disable API mocking after the tests are done
afterAll(() => server.close());

// ... rest of your setup file
```

This setup will ensure you have the MSW server running before any test suite starts and stops it after all tests are done.
Which will result in a warning in the console if you forget to mock an API request in your test suite.

```bash
[MSW] Warning: intercepted a request without a matching request handler:
 • GET https://randomuser.me/api/?results=25?results=25
```

## Conclusion

Testing a component that makes network requests in combination with MSW takes some initial preparation to configure and describe the overridden networks.
We can achieve that by using MSW's request handlers and intercepting APIs.

Once up and running we gain full grip over the network requests, their responses, statuses.
Doing so is crucial to be able to test how our application behaves in different
scenarios, such as when the request is successful or when it fails.

When global configuration is in place, MSW's will also warn us when an unhandled network requests has occurred throughout a test suite.

## Further Reading and Alternatives

Explore more advanced scenarios for mocking network requests with MSW:

- MSW's Basics - [Intercepting requests](https://mswjs.io/docs/basics/intercepting-requests) and/or [Mocking responses](https://mswjs.io/docs/basics/mocking-responses)
- MSW's Network behavior - how to describe [REST](https://mswjs.io/docs/network-behavior/rest) and/or [GraphQL](https://mswjs.io/docs/network-behavior/graphql) APIs



---
url: /react-native-testing-library/cookbook/state-management/jotai.md
---

# Jotai

## Introduction

Jotai is a global state management library for React that uses an atomic approach to optimize
renders and solve issues like extra re-renders and the need for memoization. It scales from simple
state management to complex enterprise applications, offering utilities and extensions to enhance
the developer experience.

## Task List Example

Let's assume we have a simple task list component that uses Jotai for state management. The
component has a list of tasks, a text input for typing new task name and a button to add a new task to the list.

```tsx title=state-management/jotai/TaskList.tsx
import * as React from 'react';
import { Pressable, Text, TextInput, View } from 'react-native';
import { useAtom } from 'jotai';
import { nanoid } from 'nanoid';
import { newTaskTitleAtom, tasksAtom } from './state';

export function TaskList() {
  const [tasks, setTasks] = useAtom(tasksAtom);
  const [newTaskTitle, setNewTaskTitle] = useAtom(newTaskTitleAtom);

  const handleAddTask = () => {
    setTasks((tasks) => [
      ...tasks,
      {
        id: nanoid(),
        title: newTaskTitle,
      },
    ]);
    setNewTaskTitle('');
  };

  return (
    <View>
      {tasks.map((task) => (
        <Text key={task.id} testID="task-item">
          {task.title}
        </Text>
      ))}

      {!tasks.length ? <Text>No tasks, start by adding one...</Text> : null}

      <TextInput
        accessibilityLabel="New Task"
        placeholder="New Task..."
        value={newTaskTitle}
        onChangeText={(text) => setNewTaskTitle(text)}
      />

      <Pressable accessibilityRole="button" onPress={handleAddTask}>
        <Text>Add Task</Text>
      </Pressable>
    </View>
  );
}
```

## Starting with a Simple Test

We can test our `TaskList` component using React Native Testing Library's (RNTL) regular `render`
function. Although it is sufficient to test the empty state of the `TaskList` component, it is not
enough to test the component with initial tasks present in the list.

```tsx title=state-management/jotai/__tests__/TaskList.test.tsx
import * as React from 'react';
import { render, screen, userEvent } from '@testing-library/react-native';
import { renderWithAtoms } from './test-utils';
import { TaskList } from '../TaskList';
import { newTaskTitleAtom, tasksAtom } from '../state';
import { Task } from '../types';

jest.useFakeTimers();

test('renders an empty task list', async () => {
  await render(<TaskList />);
  expect(screen.getByText(/no tasks, start by adding one/i)).toBeOnTheScreen();
});
```

## Custom Render Function to populate Jotai Atoms with Initial Values

To test the `TaskList` component with initial tasks, we need to be able to populate the `tasksAtom` with
initial values. We can create a custom render function that uses Jotai's `useHydrateAtoms` hook to
hydrate the atoms with initial values. This function will accept the initial atoms and their
corresponding values as an argument.

```tsx title=state-management/jotai/__tests__/test-utils.tsx
import * as React from 'react';
import { render } from '@testing-library/react-native';
import { useHydrateAtoms } from 'jotai/utils';
import { PrimitiveAtom } from 'jotai/vanilla/atom';

// Jotai types are not well exported, so we will make our life easier by using `any`.
export type AtomInitialValueTuple<T> = [PrimitiveAtom<T>, T];

export interface RenderWithAtomsOptions {
  initialValues: AtomInitialValueTuple<any>[];
}

/**
 * Renders a React component with Jotai atoms for testing purposes.
 *
 * @param component - The React component to render.
 * @param options - The render options including the initial atom values.
 * @returns The render result from `@testing-library/react-native`.
 */
export async function renderWithAtoms<T>(
  component: React.ReactElement,
  options: RenderWithAtomsOptions,
) {
  return await render(
    <HydrateAtomsWrapper initialValues={options.initialValues}>{component}</HydrateAtomsWrapper>,
  );
}

export type HydrateAtomsWrapperProps = React.PropsWithChildren<{
  initialValues: AtomInitialValueTuple<unknown>[];
}>;

/**
 * A wrapper component that hydrates Jotai atoms with initial values.
 *
 * @param initialValues - The initial values for the Jotai atoms.
 * @param children - The child components to render.
 * @returns The rendered children.

 */
function HydrateAtomsWrapper({ initialValues, children }: HydrateAtomsWrapperProps) {
  useHydrateAtoms(initialValues);
  return children;
}
```

## Testing the `TaskList` Component with initial tasks

We can now use the `renderWithAtoms` function to render the `TaskList` component with initial tasks. The
`initialValues` property will contain the `tasksAtom`, `newTaskTitleAtom` and their initial values. We can then test the component to ensure that the initial tasks are rendered correctly.

:::info
In our test, we populated only one atom and its initial value, but you can add other Jotai atoms and their corresponding values to the initialValues array as needed.
:::

```tsx title=state-management/jotai/__tests__/TaskList.test.tsx
const INITIAL_TASKS: Task[] = [{ id: '1', title: 'Buy bread' }];

test('renders a to do list with 1 items initially, and adds a new item', async () => {
  await renderWithAtoms(<TaskList />, {
    initialValues: [
      [tasksAtom, INITIAL_TASKS],
      [newTaskTitleAtom, ''],
    ],
  });

  expect(screen.getByText(/buy bread/i)).toBeOnTheScreen();
  expect(screen.getAllByTestId('task-item')).toHaveLength(1);

  const user = userEvent.setup();
  await user.type(screen.getByPlaceholderText(/new task/i), 'Buy almond milk');
  await user.press(screen.getByRole('button', { name: /add task/i }));

  expect(screen.getByText(/buy almond milk/i)).toBeOnTheScreen();
  expect(screen.getAllByTestId('task-item')).toHaveLength(2);
});
```

## Modifying atom outside of React components

In several cases, you might need to change an atom's state outside a React component. In our case,
we have a set of functions to get tasks and set tasks, which change the state of the task list atom.

```tsx title=state-management/jotai/state.ts
import { atom, createStore } from 'jotai';
import { Task } from './types';

export const tasksAtom = atom<Task[]>([]);
export const newTaskTitleAtom = atom('');

// Available for use outside React components
export const store = createStore();

// Selectors
export function getAllTasks(): Task[] {
  return store.get(tasksAtom);
}

// Actions
export function addTask(task: Task) {
  store.set(tasksAtom, [...getAllTasks(), task]);
}
```

## Testing atom outside of React components

You can test the `getAllTasks` and `addTask` functions outside the React component's scope by setting
the initial to-do items in the store and then checking if the functions work as expected.
No special setup is required to test these functions, as `store.set` is available by default by
Jotai.

```tsx title=state-management/jotai/__tests__/TaskList.test.tsx
import { addTask, getAllTasks, store, tasksAtom } from '../state';

//...

test('modify store outside of React component', () => {
  // Set the initial to do items in the store
  store.set(tasksAtom, INITIAL_TASKS);
  expect(getAllTasks()).toEqual(INITIAL_TASKS);

  const NEW_TASK = { id: '2', title: 'Buy almond milk' };
  addTask(NEW_TASK);
  expect(getAllTasks()).toEqual([...INITIAL_TASKS, NEW_TASK]);
});
```

## Conclusion

Testing a component or a function that depends on Jotai atoms is straightforward with the help of
the `useHydrateAtoms` hook. We've seen how to create a custom render function `renderWithAtoms` that
sets up atoms and their initial values for testing purposes. We've also seen how to test functions
that change the state of atoms outside React components. This approach allows us to test components
in different states and scenarios, ensuring they behave as expected.



---
url: /react-native-testing-library/index.md
---

# JavaScript Integration testing for React Native

> The more your tests resemble the way your software is used, the more confidence they can give you.<br/>— Kent C. Dodds

[Quick Start](/docs/start/quick-start) | [Explore API](/docs/api)

## Features

- <img src="/react-native-testing-library/img/icon-code.svg" width="36" /> **Maintainable**: Write maintainable tests for your React Native apps.
- <img src="/react-native-testing-library/img/icon-check-double.svg" width="36" /> **Reliable**: Promotes testing public APIs and avoiding implementation details.
- <img src="/react-native-testing-library/img/icon-users.svg" width="36" /> **Community Driven**: Supported by React Native community and its core contributors.



---
url: /react-native-testing-library/404.md
---

404

# PAGE NOT FOUND

[Take me home](/react-native-testing-library/)

