Testing with Jest & React Native Testing Library

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

Setup Navigators

Navigators Complete | Code
js
1
import {
2
Text,
3
View,
4
FlatList,
5
StyleSheet,
6
TouchableOpacity,
7
} from 'react-native';
8
9
import * as React from 'react';
10
import 'react-native-gesture-handler';
11
import { createStackNavigator } from '@react-navigation/stack';
12
13
const { Screen, Navigator } = createStackNavigator();
14
15
export 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
26
function 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
52
function 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
63
const 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
77
const styles3 = StyleSheet.create({
78
header: {
79
fontSize: 20,
80
textAlign: 'center',
81
marginVertical: 16,
82
},
83
body: {
84
textAlign: 'center',
85
},
86
});

Setup Tests

Tests Complete | Code

Tests

js
1
import * as React from 'react';
2
import { NavigationContainer } from '@react-navigation/native';
3
4
import { act, render, screen, fireEvent } from '@testing-library/react-native';
5
6
import '@testing-library/jest-native/extend-expect';
7
8
import Navigation from './demo-navigation';
9
10
describe('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
});
js
1
function getTestSafePlatform() {
2
if (process.env.NODE_ENV == 'test') return true;
3
return Platform.OS === 'ios';
4
}