Writing tests
React Navigation components can be tested in a similar way to other React components. This guide will cover how to write tests for components using React Navigation using Jest.
Guiding principles
When writing tests, it's encouraged to write tests that closely resemble how users interact with your app. Keeping this in mind, here are some guiding principles to follow:
- Test the result, not the action: Instead of checking if a specific navigation action was called, check if the expected components are rendered after navigation.
- Avoid mocking React Navigation: Mocking React Navigation components can lead to tests that don't match the actual logic. Instead, use a real navigator in your tests.
Following these principles will help you write tests that are more reliable and easier to maintain by avoiding testing implementation details.
Setting up Jest
Compiling React Navigation
React Navigation ships ES modules. However, Jest does not support ES modules natively.
It's necessary to transform the code to CommonJS to use them in tests. The react-native preset for Jest does not transform the code in node_modules by default. To enable this, you need to add the transformIgnorePatterns option in your Jest configuration where you can specify a regexp pattern. To compile React Navigation packages, you can add @react-navigation to the regexp.
This is usually done in a jest.config.js file or the jest key in package.json:
{
"preset": "react-native",
"transformIgnorePatterns": [
"node_modules/(?!(@react-native|react-native|@react-navigation)/)"
]
}
Mocking native dependencies
To be able to test React Navigation components, certain dependencies will need to be mocked depending on which components are being used.
If you're using @react-navigation/stack, you will need to mock:
react-native-gesture-handler
If you're using @react-navigation/drawer, you will need to mock:
react-native-reanimatedreact-native-gesture-handler
To add the mocks, create a file jest/setup.js (or any other file name of your choice) and paste the following code in it:
// Include this line for mocking react-native-gesture-handler
import 'react-native-gesture-handler/jestSetup';
// Include this section for mocking react-native-reanimated
import { setUpTests } from 'react-native-reanimated';
setUpTests();
// Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing
import { jest } from '@jest/globals';
jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');
Then we need to use this setup file in our jest config. You can add it under setupFilesAfterEnv option in a jest.config.js file or the jest key in package.json:
{
"preset": "react-native",
"transformIgnorePatterns": [
"node_modules/(?!(@react-native|react-native|@react-navigation)/)"
],
"setupFilesAfterEnv": ["<rootDir>/jest/setup.js"]
}
Jest will run the files specified in setupFilesAfterEnv before running your tests, so it's a good place to put your global mocks.
Mocking react-native-screens
This shouldn't be necessary in most cases. However, if you find yourself in a need to mock react-native-screens component for some reason, you should do it by adding following code in jest/setup.js file:
// Include this section for mocking react-native-screens
jest.mock('react-native-screens', () => {
// Require actual module instead of a mock
let screens = jest.requireActual('react-native-screens');
// All exports in react-native-screens are getters
// We cannot use spread for cloning as it will call the getters
// So we need to clone it with Object.create
screens = Object.create(
Object.getPrototypeOf(screens),
Object.getOwnPropertyDescriptors(screens)
);