xref: /expo/docs/pages/develop/unit-testing.mdx (revision d3ed20bf)
10680b787SAman Mittal---
20680b787SAman Mittaltitle: Unit testing
30680b787SAman Mittaldescription: Learn how to set up and configure the jest-expo package to write unit tests and snapshot tests for a project.
40680b787SAman Mittal---
50680b787SAman Mittal
60680b787SAman Mittalimport { Terminal } from '~/ui/components/Snippet';
70680b787SAman Mittalimport { BoxLink } from '~/ui/components/BoxLink';
8*d3ed20bfSAman Mittalimport { FileTree } from '~/ui/components/FileTree';
9*d3ed20bfSAman Mittalimport { Tabs, Tab } from '~/ui/components/Tabs';
100680b787SAman Mittal
110680b787SAman Mittal[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.
120680b787SAman Mittal
130680b787SAman MittalYou'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.
140680b787SAman Mittal
150680b787SAman Mittal## Installation
160680b787SAman Mittal
17*d3ed20bfSAman MittalTo install `jest-expo` in your project, run the following command:
180680b787SAman Mittal
190680b787SAman Mittal<Terminal cmd={['$ npx expo install jest-expo jest']} />
200680b787SAman Mittal
21*d3ed20bfSAman Mittal> **info** If you are using TypeScript, then also install `@types/jest` as a dev dependency.
220680b787SAman Mittal
23*d3ed20bfSAman MittalThen, update **package.json** to add a script for running tests and add the preset for using the base configuration from `jest-expo`:
24*d3ed20bfSAman Mittal
25*d3ed20bfSAman Mittal{/* prettier-ignore */}
260680b787SAman Mittal```json package.json
270680b787SAman Mittal"scripts": {
28*d3ed20bfSAman Mittal  /* @hide ... */ /* @end */
290680b787SAman Mittal  "test": "jest"
300680b787SAman Mittal},
31*d3ed20bfSAman Mittal/* @hide ... */ /* @end */
320680b787SAman Mittal"jest": {
330680b787SAman Mittal  "preset": "jest-expo"
340680b787SAman Mittal}
350680b787SAman Mittal```
360680b787SAman Mittal
370680b787SAman Mittal## Configuration
380680b787SAman Mittal
390680b787SAman MittalA 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:
400680b787SAman Mittal
41*d3ed20bfSAman Mittal<Tabs>
42*d3ed20bfSAman Mittal
43*d3ed20bfSAman Mittal<Tab label="npm/Yarn">
44*d3ed20bfSAman Mittal
450680b787SAman Mittal```json package.json
460680b787SAman Mittal"jest": {
470680b787SAman Mittal  "preset": "jest-expo",
480680b787SAman Mittal  "transformIgnorePatterns": [
490680b787SAman Mittal    "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)"
500680b787SAman Mittal  ]
510680b787SAman Mittal}
520680b787SAman Mittal```
530680b787SAman Mittal
54*d3ed20bfSAman Mittal</Tab>
550680b787SAman Mittal
56*d3ed20bfSAman Mittal<Tab label="pnpm">
57*d3ed20bfSAman Mittal
58*d3ed20bfSAman Mittal```json package.json
59*d3ed20bfSAman Mittal"jest": {
60*d3ed20bfSAman Mittal  "preset": "jest-expo",
61*d3ed20bfSAman Mittal  "transformIgnorePatterns": [
62*d3ed20bfSAman Mittal    "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))"
63*d3ed20bfSAman Mittal  ]
64*d3ed20bfSAman Mittal}
65*d3ed20bfSAman Mittal```
66*d3ed20bfSAman Mittal
67*d3ed20bfSAman Mittal</Tab>
68*d3ed20bfSAman Mittal
69*d3ed20bfSAman Mittal</Tabs>
70*d3ed20bfSAman Mittal
71*d3ed20bfSAman MittalJest has various configuration options. The above configuration covers the majority of your needs. However, you can always add to this pattern list. For more details, see [Configuring Jest](https://jestjs.io/docs/configuration).
720680b787SAman Mittal
730680b787SAman Mittal## Unit test
740680b787SAman Mittal
750680b787SAman MittalA unit test is used to check the smallest unit of code, usually a function.
760680b787SAman Mittal
770680b787SAman MittalTo 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).
780680b787SAman Mittal
790680b787SAman MittalThe test will expect the state of the `<App />` component to have one child element:
800680b787SAman Mittal
810680b787SAman Mittal```js App.test.js
820680b787SAman Mittalimport React from 'react';
830680b787SAman Mittalimport renderer from 'react-test-renderer';
840680b787SAman Mittal
850680b787SAman Mittalimport App from './App';
860680b787SAman Mittal
870680b787SAman Mittaldescribe('<App />', () => {
880680b787SAman Mittal  it('has 1 child', () => {
890680b787SAman Mittal    const tree = renderer.create(<App />).toJSON();
900680b787SAman Mittal    expect(tree.children.length).toBe(1);
910680b787SAman Mittal  });
920680b787SAman Mittal});
930680b787SAman Mittal```
940680b787SAman Mittal
95*d3ed20bfSAman Mittal> **info** If you are using TypeScript, use **.ts** or **.tsx** file extension.
96*d3ed20bfSAman Mittal
970680b787SAman MittalTo run the test:
980680b787SAman Mittal
990680b787SAman Mittal<Terminal cmd={['$ npm run test']} />
1000680b787SAman Mittal
1010680b787SAman MittalIf everything goes well, you should see the one test passed. For more information, see [expect and conditional matchers](https://jestjs.io/docs/en/expect).
1020680b787SAman Mittal
1030680b787SAman Mittal## Structure your tests
1040680b787SAman Mittal
1050680b787SAman MittalRight 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.
1060680b787SAman Mittal
1070680b787SAman MittalAn example structure is shown below:
1080680b787SAman Mittal
109*d3ed20bfSAman Mittal<FileTree
110*d3ed20bfSAman Mittal  files={[
111c7504584SBartosz Kaszubowski    '__tests__/components/button.test.js',
112c7504584SBartosz Kaszubowski    '__tests__/navigation/mainstack.test.js',
113c7504584SBartosz Kaszubowski    '__tests__/screens/home.test.js',
114c7504584SBartosz Kaszubowski    'src/components/button.js',
115c7504584SBartosz Kaszubowski    'src/navigation/mainstack.js',
116*d3ed20bfSAman Mittal    'src/screens/home.js',
117*d3ed20bfSAman Mittal  ]}
118*d3ed20bfSAman Mittal/>
1190680b787SAman Mittal
1200680b787SAman MittalHowever, this approach causes a lot of long import paths, such as `../../src/components/button`.
1210680b787SAman Mittal
1220680b787SAman MittalAlternatively, 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:
1230680b787SAman Mittal
124*d3ed20bfSAman Mittal<FileTree files={['src/components/button.js', 'src/components/__tests__/button.test.js']} />
1250680b787SAman Mittal
1260680b787SAman MittalNow, if you move **\_\_tests\_\_** within the **components** directory, the import path of `<Button>` in the the **button.test.js** will be `../button`.
1270680b787SAman Mittal
1280680b787SAman MittalAnother option for test/file structure:
1290680b787SAman Mittal
130*d3ed20bfSAman Mittal<FileTree
131*d3ed20bfSAman Mittal  files={[
132c7504584SBartosz Kaszubowski    'src/components/button.js',
133c7504584SBartosz Kaszubowski    'src/components/button.style.js',
134c7504584SBartosz Kaszubowski    'src/components/button.test.js',
135*d3ed20bfSAman Mittal  ]}
136*d3ed20bfSAman Mittal/>
1370680b787SAman Mittal
1380680b787SAman MittalIt's all about preferences and up to you to decide how you want to organize your project directory.
1390680b787SAman Mittal
1400680b787SAman Mittal## Snapshot test
1410680b787SAman Mittal
1420680b787SAman MittalA 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).
1430680b787SAman Mittal
1440680b787SAman MittalTo add a snapshot test for `<App />`, add the following code snippet in the `describe()` in **App.test.js**:
1450680b787SAman Mittal
1460680b787SAman Mittal```js App.test.js
1470680b787SAman Mittalit('renders correctly', () => {
1480680b787SAman Mittal  const tree = renderer.create(<App />).toJSON();
1490680b787SAman Mittal  expect(tree).toMatchSnapshot();
1500680b787SAman Mittal});
1510680b787SAman Mittal```
1520680b787SAman Mittal
1530680b787SAman MittalRun `npm run test` command, and if everything goes well, you should see a snapshot created and two tests passed.
1540680b787SAman Mittal
1550680b787SAman Mittal## Code coverage reports
1560680b787SAman Mittal
1570680b787SAman MittalCode coverage reports can help you understand how much of your code is tested.
1580680b787SAman Mittal
1590680b787SAman MittalIf you'd like to see code coverage report in your project using the HTML format, add the following to the **package.json**:
1600680b787SAman Mittal
1610680b787SAman Mittal```json package.json
1620680b787SAman Mittal"jest": {
1630680b787SAman Mittal  ...
1640680b787SAman Mittal  "collectCoverage": true,
1650680b787SAman Mittal  "collectCoverageFrom": [
1660680b787SAman Mittal    "**/*.{js,jsx}",
1670680b787SAman Mittal    "!**/coverage/**",
1680680b787SAman Mittal    "!**/node_modules/**",
1690680b787SAman Mittal    "!**/babel.config.js",
1700680b787SAman Mittal    "!**/jest.setup.js"
1710680b787SAman Mittal  ]
1720680b787SAman Mittal}
1730680b787SAman Mittal```
1740680b787SAman Mittal
1750680b787SAman MittalAdding 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.
1760680b787SAman Mittal
1770680b787SAman MittalRun `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.
1780680b787SAman Mittal
1790680b787SAman Mittal> 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.
1800680b787SAman Mittal
1810680b787SAman Mittal## Optional: Jest flows
1820680b787SAman Mittal
1830680b787SAman MittalYou can also use different flows to run your tests. Below are a few example scripts that you can try:
1840680b787SAman Mittal
1850680b787SAman Mittal```json package.json
1860680b787SAman Mittal"scripts": {
1870680b787SAman Mittal  ...
1880680b787SAman Mittal  // active development of tests, watch files for changes and re-runs all tests
1890680b787SAman Mittal  "test": "jest --watch --coverage=false --changedSince=origin/main",
1900680b787SAman Mittal
1910680b787SAman Mittal  // debug, console.logs and only re-runs the file that was changed
1920680b787SAman Mittal  "testDebug": "jest -o --watch --coverage=false",
1930680b787SAman Mittal
1940680b787SAman Mittal  // displays code coverage in cli and updates the code coverage html
1950680b787SAman Mittal  "testFinal": "jest",
1960680b787SAman Mittal
1970680b787SAman Mittal  // when a screen/component is updated, the test snapshots will throw an error, this updates them
1980680b787SAman Mittal  "updateSnapshots": "jest -u --coverage=false"
1990680b787SAman Mittal}
2000680b787SAman Mittal```
2010680b787SAman Mittal
2020680b787SAman MittalFor more information, see [CLI Options](https://jestjs.io/docs/en/cli) in Jest documentation.
2030680b787SAman Mittal
2040680b787SAman Mittal## Next step
2050680b787SAman Mittal
2060680b787SAman Mittal<BoxLink
2070680b787SAman Mittal  title="React Native Testing library"
2080680b787SAman Mittal  description="You can also use React Native Testing Library which provides testing utilities that encourage good testing practices and works with Jest."
2090680b787SAman Mittal  href="https://github.com/callstack/react-native-testing-library"
2100680b787SAman Mittal/>
211