mirror of
https://github.com/callstack/react-native-testing-library.git
synced 2026-09-18 23:09:04 +08:00
docs: refactor react-navigation example with React Nav team feedback (#1253)
This commit is contained in:
committed by
GitHub
parent
6fba252653
commit
a218bbcf57
@@ -1,9 +1,16 @@
|
||||
# RNTL example app for React Navigation
|
||||
|
||||
This example shows how to write integration tests using React Navigation without mocking it.
|
||||
This example shows how to write integration tests using React Navigation without mocking it. Presented approach has been consulted with and influenced by React Navigation team.
|
||||
|
||||
There are two types of tests:
|
||||
1. integration tests operating on whole navigators, they should use `renderNavigator` helper to render a navigator component used in the app. It is useful when you want to test a scenario that includes multiple screens.
|
||||
2. single screen tests where you would pass mock `navigation` prop, built using `buildNavigationMock()` helper, and `route` prop to the screen component using regular `render` function.
|
||||
## Recommended tests
|
||||
|
||||
There are two types of recommeded tests:
|
||||
1. Tests operating on navigator level - these use `renderNavigator` helper to render a navigator component used in the app. It is useful when you want to test a scenario that includes multiple screens.
|
||||
2. Tests operating on single screen level - these use regular `render` helper but require refactoring screen components into `Screen` and `ScreenContent` components. Where `Screen` receives React Navigation props and/or uses hooks like `useNavigation` while `ScreenContent` does not have a direct relation to React Navigation API but gets props from `Screen` and calls relevant callbacks to trigger navigation.
|
||||
|
||||
> Note that this example applies `includeHiddenElements: false` by default, so all queries will ignore elements on the hidden screens, e.g. inactive tabs or screens present in stack navigators. This option is enabled in `jest-setup.js` file, using `defaultIncludeHiddenElements: false` option to `configure` function.
|
||||
|
||||
## Non-recommended tests
|
||||
|
||||
There also exists another popular type of screen level tests, where users mock React Navigation objects like `navigation`, `route` and/or hooks like `useNavigation`, etc. We don't recommend this way of testing. **Mocking internal parts of the libraries is effectively testing implementation details, which goes against the Testing Library's [Guiding Principles](https://testing-library.com/docs/guiding-principles/)**.
|
||||
|
||||
|
||||
+9
-7
@@ -1,10 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet, View, Text, Pressable } from 'react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
export default function DetailsScreen({ route }) {
|
||||
export default function DetailsScreen({ navigation, route }) {
|
||||
const item = route.params;
|
||||
return (
|
||||
<DetailsScreenContent item={item} onGoBack={() => navigation.goBack()} />
|
||||
);
|
||||
}
|
||||
|
||||
export function DetailsScreenContent({ item, onGoBack }) {
|
||||
return (
|
||||
<View>
|
||||
<Text accessibilityRole="header" style={styles.header}>
|
||||
@@ -14,16 +18,14 @@ export default function DetailsScreen({ route }) {
|
||||
The number you have chosen is {item.value}.
|
||||
</Text>
|
||||
|
||||
<BackButton />
|
||||
<BackButton onPress={onGoBack} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function BackButton() {
|
||||
const navigation = useNavigation();
|
||||
|
||||
function BackButton({ onPress }) {
|
||||
return (
|
||||
<Pressable accessibilityRole="button" onPress={() => navigation.goBack()}>
|
||||
<Pressable accessibilityRole="button" onPress={onPress}>
|
||||
<Text>Go Back</Text>
|
||||
</Pressable>
|
||||
);
|
||||
|
||||
@@ -1,35 +1,18 @@
|
||||
import * as React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import { buildNavigationMock } from '../test-utils';
|
||||
import DetailsScreen from './DetailsScreen';
|
||||
|
||||
jest.mock('@react-navigation/native', () => {
|
||||
const originalModule = jest.requireActual('@react-navigation/native');
|
||||
|
||||
return {
|
||||
...originalModule,
|
||||
useNavigation: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
let navigation;
|
||||
|
||||
// Reset navigation before each test
|
||||
beforeEach(() => {
|
||||
navigation = buildNavigationMock();
|
||||
useNavigation.mockImplementation(() => navigation);
|
||||
});
|
||||
import { DetailsScreenContent } from './DetailsScreen';
|
||||
|
||||
test('Details screen contains the header and content', () => {
|
||||
const params = {
|
||||
const item = {
|
||||
id: 100,
|
||||
title: 'Item 100',
|
||||
value: 100,
|
||||
};
|
||||
|
||||
const onGoBack = jest.fn();
|
||||
|
||||
// Passing both navigation and route to the screen as props
|
||||
render(<DetailsScreen navigation={navigation} route={{ params }} />);
|
||||
render(<DetailsScreenContent item={item} onGoBack={onGoBack} />);
|
||||
|
||||
expect(
|
||||
screen.getByRole('header', { name: 'Details for Item 100' })
|
||||
@@ -38,5 +21,5 @@ test('Details screen contains the header and content', () => {
|
||||
|
||||
// Note: Go Back button get navigation from `useNavigation` hook
|
||||
fireEvent.press(screen.getByRole('button', { name: 'Go Back' }));
|
||||
expect(navigation.goBack).toHaveBeenCalledTimes(1);
|
||||
expect(onGoBack).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
-13
@@ -10,16 +10,3 @@ import { render } from '@testing-library/react-native';
|
||||
export function renderNavigator(ui) {
|
||||
return render(<NavigationContainer>{ui}</NavigationContainer>);
|
||||
}
|
||||
|
||||
export function buildNavigationMock() {
|
||||
return {
|
||||
navigate: jest.fn(),
|
||||
reset: jest.fn(),
|
||||
goBack: jest.fn(),
|
||||
dispatch: jest.fn(),
|
||||
isFocused: jest.fn(() => true),
|
||||
setParams: jest.fn(),
|
||||
setOptions: jest.fn(),
|
||||
addListener: jest.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user