xref: /expo/docs/pages/develop/unit-testing.mdx (revision fb2cccd0)
1---
2title: Unit testing
3description: Learn how to set up and configure the jest-expo package to write unit tests and snapshot tests for a project.
4---
5
6import { Terminal } from '~/ui/components/Snippet';
7import { BoxLink } from '~/ui/components/BoxLink';
8import { FileTree } from "~/ui/components/FileTree";
9
10[Jest](https://jestjs.io) is the most widely used JavaScript unit testing framework. In this guide, you'll learn how to set up Jest in your project, write a unit test, write a snapshot test, and best practices for structuring your tests when using Jest with React Native.
11
12You'll also use the `jest-expo` package which is a Jest preset and mocks the native part of the Expo SDK and handles most of the configuration.
13
14## Installation
15
16To install a compatible version of `jest-expo` for your project, run the following command:
17
18<Terminal cmd={['$ npx expo install jest-expo jest']} />
19
20Then, update **package.json** to include:
21
22```json package.json
23"scripts": {
24  ...
25  "test": "jest"
26},
27"jest": {
28  "preset": "jest-expo"
29}
30```
31
32You can now start writing Jest tests.
33
34## Configuration
35
36A starting configuration you can use is to make sure any modules you are using within the **node_modules** directory are transpiled when running Jest. This can be done by including the [`transformIgnorePatterns`](https://jestjs.io/docs/configuration#transformignorepatterns-arraystring) property that takes a regex pattern as its value:
37
38```json package.json
39"jest": {
40  "preset": "jest-expo",
41  "transformIgnorePatterns": [
42    "node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)"
43    // In case you are using pnpm as package manager, use this instead:
44    // "node_modules/(?!(?:.pnpm/)?((jest-)?react-native|@react-native(-community)?|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg))"
45  ]
46}
47```
48
49The above configuration should cover the majority of your needs, however, you can always add to this pattern list.
50
51Jest comes with a lot of configuration options. For more details, see [Configuring Jest](https://jestjs.io/docs/configuration).
52
53## Unit test
54
55A unit test is used to check the smallest unit of code, usually a function.
56
57To write your first unit test, start by writing a simple test for **App.js**. Create a test file for it and call it **App.test.js**. Jest identifies a file with the **.test.js** extension as a test and includes it in the tests queue. There are also other ways to [structure a test](#structure-your-tests).
58
59The test will expect the state of the `<App />` component to have one child element:
60
61```js App.test.js
62import React from 'react';
63import renderer from 'react-test-renderer';
64
65import App from './App';
66
67describe('<App />', () => {
68  it('has 1 child', () => {
69    const tree = renderer.create(<App />).toJSON();
70    expect(tree.children.length).toBe(1);
71  });
72});
73```
74
75To run the test:
76
77<Terminal cmd={['$ npm run test']} />
78
79If everything goes well, you should see the one test passed. For more information, see [expect and conditional matchers](https://jestjs.io/docs/en/expect).
80
81## Structure your tests
82
83Right now, you have a single test file in the project directory. Adding more test files can make it hard to organize your project directory. The easiest way to avoid this is to create a **\_\_tests\_\_** directory and put all your tests inside it.
84
85An example structure is shown below:
86
87<FileTree files={[
88  '__tests__/components/button.test.js',
89  '__tests__/navigation/mainstack.test.js',
90  '__tests__/screens/home.test.js',
91  'src/components/button.js',
92  'src/navigation/mainstack.js',
93  'src/screens/home.js'
94]}/>
95
96However, this approach causes a lot of long import paths, such as `../../src/components/button`.
97
98Alternatively, you can have multiple **\_\_tests\_\_** sub-directories for different areas of your project. For example, create a separate test directory for **components**, **navigation**, and so on:
99
100<FileTree files={[
101  'src/components/button.js',
102  'src/components/__tests__/button.test.js'
103]}/>
104
105Now, if you move **\_\_tests\_\_** within the **components** directory, the import path of `<Button>` in the the **button.test.js** will be `../button`.
106
107Another option for test/file structure:
108
109<FileTree files={[
110  'src/components/button.js',
111  'src/components/button.style.js',
112  'src/components/button.test.js',
113]}/>
114
115It's all about preferences and up to you to decide how you want to organize your project directory.
116
117## Snapshot test
118
119A snapshot test is used to make sure that UI stays consistent, especially when a project is working with global styles that are potentially shared across components. For more information, see [snapshot testing](https://jestjs.io/docs/en/snapshot-testing).
120
121To add a snapshot test for `<App />`, add the following code snippet in the `describe()` in **App.test.js**:
122
123```js App.test.js
124it('renders correctly', () => {
125  const tree = renderer.create(<App />).toJSON();
126  expect(tree).toMatchSnapshot();
127});
128```
129
130Run `npm run test` command, and if everything goes well, you should see a snapshot created and two tests passed.
131
132## Code coverage reports
133
134Code coverage reports can help you understand how much of your code is tested.
135
136If you'd like to see code coverage report in your project using the HTML format, add the following to the **package.json**:
137
138```json package.json
139"jest": {
140  ...
141  "collectCoverage": true,
142  "collectCoverageFrom": [
143    "**/*.{js,jsx}",
144    "!**/coverage/**",
145    "!**/node_modules/**",
146    "!**/babel.config.js",
147    "!**/jest.setup.js"
148  ]
149}
150```
151
152Adding the above snippet allows Jest to collect coverage of all **.js** and **.jsx** files that are not inside the **coverage** or **node_modules** directories. It also excludes the **babel.config.js** and **jest.setup.js** files. You can add or remove more to this list to match your needs.
153
154Run `npm run test`. You should see a **coverage** directory created in your project. Find the **index.html** file within this directory and double-click to open it up in a browser to see the coverage report.
155
156> Usually, we don't recommend uploading **index.html** file to git. To prevent it from being tracked, you can add `coverage/**/*` in the **.gitignore** file.
157
158## Optional: Jest flows
159
160You can also use different flows to run your tests. Below are a few example scripts that you can try:
161
162```json package.json
163"scripts": {
164  ...
165  // active development of tests, watch files for changes and re-runs all tests
166  "test": "jest --watch --coverage=false --changedSince=origin/main",
167
168  // debug, console.logs and only re-runs the file that was changed
169  "testDebug": "jest -o --watch --coverage=false",
170
171  // displays code coverage in cli and updates the code coverage html
172  "testFinal": "jest",
173
174  // when a screen/component is updated, the test snapshots will throw an error, this updates them
175  "updateSnapshots": "jest -u --coverage=false"
176}
177```
178
179For more information, see [CLI Options](https://jestjs.io/docs/en/cli) in Jest documentation.
180
181## Next step
182
183<BoxLink
184  title="React Native Testing library"
185  description="You can also use React Native Testing Library which provides testing utilities that encourage good testing practices and works with Jest."
186  href="https://github.com/callstack/react-native-testing-library"
187/>
188