Testing is tough. Doing it right is tougher.
Testing is already hard.
Adding tests to a React Native project is harder when accounting for navigation, Redux, and hydration.
Here is how I've done it
1import {
2 Text,
3 View,
4 FlatList,
5 StyleSheet,
6 TouchableOpacity,
7} from 'react-native';
8
9import * as React from 'react';
10import 'react-native-gesture-handler';
11import { createStackNavigator } from '@react-navigation/stack';
12
13const { Screen, Navigator } = createStackNavigator();
14
15export default function Navigation() {
16 const options = {};
17
18 return (
19 <Navigator>
20 <Screen name="Home" component={HomeScreen} />
21 <Screen options={options} name="Details" component={DetailsScreen} />
22 </Navigator>
23 );
24}
25
26function HomeScreen({ navigation }) {
27 const [items] = React.useState(
28 new Array(20).fill(null).map((_, idx) => idx + 1)
29 );
30
31 const onOpacityPress = (item) => navigation.navigate('Details', item);
32
33 return (
34 <View>
35 <Text style={styles2.header}>List of numbers from 1 to 20</Text>
36 <FlatList
37 keyExtractor={(_, idx) => `${idx}`}
38 data={items}
39 renderItem={({ item }) => (
40 <TouchableOpacity
41 onPress={() => onOpacityPress(item)}
42 style={styles2.row}
43 >
44 <Text>Item number {item}</Text>
45 </TouchableOpacity>
46 )}
47 />
48 </View>
49 );
50}
51
52function DetailsScreen(props) {
53 const item = Number.parseInt(props.route.params, 10);
54
55 return (
56 <View>
57 <Text style={styles3.header}>Showing details for {item}</Text>
58 <Text style={styles3.body}>the number you have chosen is {item}</Text>
59 </View>
60 );
61}
62
63const styles2 = StyleSheet.create({
64 header: {
65 fontSize: 20,
66 textAlign: 'center',
67 marginVertical: 16,
68 },
69 row: {
70 paddingVertical: 16,
71 paddingHorizontal: 24,
72 borderBottomColor: '#DDDDDD',
73 borderBottomWidth: 1,
74 },
75});
76
77const styles3 = StyleSheet.create({
78 header: {
79 fontSize: 20,
80 textAlign: 'center',
81 marginVertical: 16,
82 },
83 body: {
84 textAlign: 'center',
85 },
86});
1import * as React from 'react';
2import { NavigationContainer } from '@react-navigation/native';
3
4import { act, render, screen, fireEvent } from '@testing-library/react-native';
5
6import '@testing-library/jest-native/extend-expect';
7
8import Navigation from './demo-navigation';
9
10describe('Testing react navigation', () => {
11 test('page contains the header and 10 items', async () => {
12 const component = (
13 <NavigationContainer>
14 <Navigation />
15 </NavigationContainer>
16 );
17 render(component);
18 await act(async () => {
19 const header = await screen.findByText('List of numbers from 1 to 20');
20 const items = await screen.findAllByText(/Item number/);
21 expect(items.length).toBe(10);
22 expect(header).toBeOnTheScreen();
23 });
24 });
25
26 test('clicking on one item takes you to the details screen', async () => {
27 const component = (
28 <NavigationContainer>
29 <Navigation />
30 </NavigationContainer>
31 );
32
33 render(component);
34
35 await act(async () => {
36 const toClick = await screen.findByText('Item number 5');
37
38 await fireEvent(toClick, 'press');
39
40 const newHeader = await screen.findByText('Showing details for 5');
41 const newBody = await screen.findByText(
42 'the number you have chosen is 5'
43 );
44
45 expect(newHeader).toBeOnTheScreen();
46 expect(newBody).toBeOnTheScreen();
47 });
48 });
49});
1function getTestSafePlatform() {
2 if (process.env.NODE_ENV == 'test') return true;
3 return Platform.OS === 'ios';
4}