@testing-library/react-native 14.0.0-beta.1 → 14.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -6
- package/build/helpers/accessibility.js +24 -7
- package/build/helpers/accessibility.js.map +1 -1
- package/docs/README.md +31 -0
- package/docs/agents/architecture.md +21 -0
- package/docs/agents/build-and-validation.md +27 -0
- package/docs/agents/code-style.md +12 -0
- package/docs/agents/example-apps.md +56 -0
- package/docs/agents/git-workflow.md +13 -0
- package/docs/agents/testing.md +20 -0
- package/docs/api/accessibility.md +26 -0
- package/docs/api/async-utilities.md +137 -0
- package/docs/api/configuration.md +61 -0
- package/docs/api/fire-event.md +165 -0
- package/docs/api/jest-matchers.md +198 -0
- package/docs/api/other-helpers.md +94 -0
- package/docs/api/overview.md +18 -0
- package/docs/api/queries.md +500 -0
- package/docs/api/render-hook.md +176 -0
- package/docs/api/render.md +49 -0
- package/docs/api/screen.md +188 -0
- package/docs/api/user-event.md +295 -0
- package/docs/cookbook/async-events.md +147 -0
- package/docs/cookbook/custom-render.md +83 -0
- package/docs/cookbook/network-requests.md +375 -0
- package/docs/guides/common-mistakes.md +587 -0
- package/docs/guides/how-to-query.md +125 -0
- package/docs/guides/llm-guidelines.md +228 -0
- package/docs/guides/migration-v14.md +553 -0
- package/docs/guides/quick-start.md +77 -0
- package/docs/guides/testing-environment.md +123 -0
- package/docs/guides/troubleshooting.md +61 -0
- package/docs/guides/understanding-act.md +207 -0
- package/package.json +9 -8
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Custom `render` function
|
|
2
|
+
|
|
3
|
+
### Summary
|
|
4
|
+
|
|
5
|
+
RNTL exposes the `render` function as the primary entry point for tests. If you make complex, repeating setups for your tests, consider creating a custom render function. The idea is to encapsulate common setup steps and test wiring inside a render function suitable for your tests.
|
|
6
|
+
|
|
7
|
+
### Example
|
|
8
|
+
|
|
9
|
+
```tsx title=test-utils.ts
|
|
10
|
+
// ...
|
|
11
|
+
|
|
12
|
+
interface RenderWithProvidersProps {
|
|
13
|
+
user?: User | null;
|
|
14
|
+
theme?: Theme;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function renderWithProviders<T>(
|
|
18
|
+
ui: React.ReactElement<T>,
|
|
19
|
+
options?: RenderWithProvidersProps,
|
|
20
|
+
) {
|
|
21
|
+
return await render(
|
|
22
|
+
<UserProvider.Provider value={options?.user ?? null}>
|
|
23
|
+
<ThemeProvider.Provider value={options?.theme ?? 'light'}>{ui}</ThemeProvider.Provider>
|
|
24
|
+
</UserProvider.Provider>,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```tsx title=custom-render/index.test.tsx
|
|
30
|
+
import { screen } from '@testing-library/react-native';
|
|
31
|
+
import { renderWithProviders } from '../test-utils';
|
|
32
|
+
// ...
|
|
33
|
+
|
|
34
|
+
test('renders WelcomeScreen with user', async () => {
|
|
35
|
+
await renderWithProviders(<WelcomeScreen />, { user: { name: 'Jar-Jar' } });
|
|
36
|
+
expect(screen.getByText(/hello Jar-Jar/i)).toBeOnTheScreen();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('renders WelcomeScreen without user', async () => {
|
|
40
|
+
await renderWithProviders(<WelcomeScreen />, { user: null });
|
|
41
|
+
expect(screen.getByText(/hello stranger/i)).toBeOnTheScreen();
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Example [full source code](https://github.com/callstack/react-native-testing-library/tree/main/examples/cookbook/custom-render).
|
|
46
|
+
|
|
47
|
+
### More info
|
|
48
|
+
|
|
49
|
+
#### Additional params
|
|
50
|
+
|
|
51
|
+
A custom render function might accept additional parameters to allow for setting up different start conditions for a test, e.g., the initial state for global state management.
|
|
52
|
+
|
|
53
|
+
```tsx title=SomeScreen.test.tsx
|
|
54
|
+
test('renders SomeScreen for logged in user', async () => {
|
|
55
|
+
await renderScreen(<SomeScreen />, { state: loggedInState });
|
|
56
|
+
// ...
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### Multiple functions
|
|
61
|
+
|
|
62
|
+
Depending on the situation, you may declare more than one custom render function. For example, you have one function for testing application flows and a second for testing individual screens.
|
|
63
|
+
|
|
64
|
+
```tsx title=test-utils.tsx
|
|
65
|
+
function renderNavigator(ui, options);
|
|
66
|
+
function renderScreen(ui, options);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
#### Async setup
|
|
70
|
+
|
|
71
|
+
Since `render` is async, your custom render function should be marked as `async` and use `await render()`. This pattern also makes it easy to add additional async setup if needed:
|
|
72
|
+
|
|
73
|
+
```tsx title=SomeScreen.test.tsx
|
|
74
|
+
async function renderWithData<T>(ui: React.ReactElement<T>) {
|
|
75
|
+
const data = await fetchTestData();
|
|
76
|
+
return await render(<DataProvider value={data}>{ui}</DataProvider>);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
test('renders SomeScreen', async () => {
|
|
80
|
+
await renderWithData(<SomeScreen />);
|
|
81
|
+
// ...
|
|
82
|
+
});
|
|
83
|
+
```
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# Network Requests
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
Mocking network requests is an essential part of testing React Native applications. By mocking
|
|
6
|
+
network
|
|
7
|
+
requests, you can control the data that is returned from the server and test how your application
|
|
8
|
+
behaves in different scenarios, such as when the request is successful or when it fails.
|
|
9
|
+
|
|
10
|
+
In this guide, we will show you how to mock network requests and guard your test suits from unwanted
|
|
11
|
+
and unmocked/unhandled network requests
|
|
12
|
+
|
|
13
|
+
> [!INFO]
|
|
14
|
+
> To simulate a real-world scenario, we will use the [Random User Generator API](https://randomuser.me/) that provides random user data.
|
|
15
|
+
|
|
16
|
+
## Phonebook Example
|
|
17
|
+
|
|
18
|
+
Let's assume we have a simple phonebook application that
|
|
19
|
+
uses [`fetch`](https://reactnative.dev/docs/network#using-fetch) for fetching Data from a server.
|
|
20
|
+
In our case, we have a list of contacts and favorites that we want to display in our application.
|
|
21
|
+
|
|
22
|
+
This is how the root of the application looks like:
|
|
23
|
+
|
|
24
|
+
```tsx title=network-requests/Phonebook.tsx
|
|
25
|
+
import React, { useEffect, useState } from 'react';
|
|
26
|
+
import { Text } from 'react-native';
|
|
27
|
+
import { User } from './types';
|
|
28
|
+
import ContactsList from './components/ContactsList';
|
|
29
|
+
import FavoritesList from './components/FavoritesList';
|
|
30
|
+
import getAllContacts from './api/getAllContacts';
|
|
31
|
+
import getAllFavorites from './api/getAllFavorites';
|
|
32
|
+
|
|
33
|
+
export default () => {
|
|
34
|
+
const [usersData, setUsersData] = useState<User[]>([]);
|
|
35
|
+
const [favoritesData, setFavoritesData] = useState<User[]>([]);
|
|
36
|
+
const [error, setError] = useState<string | null>(null);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const _getAllContacts = async () => {
|
|
40
|
+
const _data = await getAllContacts();
|
|
41
|
+
setUsersData(_data);
|
|
42
|
+
};
|
|
43
|
+
const _getAllFavorites = async () => {
|
|
44
|
+
const _data = await getAllFavorites();
|
|
45
|
+
setFavoritesData(_data);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const run = async () => {
|
|
49
|
+
try {
|
|
50
|
+
await Promise.all([_getAllContacts(), _getAllFavorites()]);
|
|
51
|
+
} catch (e) {
|
|
52
|
+
const message = isErrorWithMessage(e) ? e.message : 'Something went wrong';
|
|
53
|
+
setError(message);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
void run();
|
|
58
|
+
}, []);
|
|
59
|
+
|
|
60
|
+
if (error) {
|
|
61
|
+
return <Text>An error occurred: {error}</Text>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<>
|
|
66
|
+
<FavoritesList users={favoritesData} />
|
|
67
|
+
<ContactsList users={usersData} />
|
|
68
|
+
</>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
We fetch the contacts from the server using the `getAllFavorites` function that utilizes `fetch`.
|
|
74
|
+
|
|
75
|
+
```tsx title=network-requests/api/getAllContacts.ts
|
|
76
|
+
import { User } from '../types';
|
|
77
|
+
|
|
78
|
+
export default async (): Promise<User[]> => {
|
|
79
|
+
const res = await fetch('https://randomuser.me/api/?results=25');
|
|
80
|
+
if (!res.ok) {
|
|
81
|
+
throw new Error(`Error fetching contacts`);
|
|
82
|
+
}
|
|
83
|
+
const json = await res.json();
|
|
84
|
+
return json.results;
|
|
85
|
+
};
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
We have similar function for fetching the favorites, but this time limiting the results to 10.
|
|
89
|
+
|
|
90
|
+
```tsx title=network-requests/api/getAllFavorites.ts
|
|
91
|
+
import { User } from '../types';
|
|
92
|
+
|
|
93
|
+
export default async (): Promise<User[]> => {
|
|
94
|
+
const res = await fetch('https://randomuser.me/api/?results=10');
|
|
95
|
+
if (!res.ok) {
|
|
96
|
+
throw new Error(`Error fetching favorites`);
|
|
97
|
+
}
|
|
98
|
+
const json = await res.json();
|
|
99
|
+
return json.results;
|
|
100
|
+
};
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Our `FavoritesList` component is a simple component that displays the list of favorite contacts and
|
|
104
|
+
their avatars horizontally.
|
|
105
|
+
|
|
106
|
+
```tsx title=network-requests/components/FavoritesList.tsx
|
|
107
|
+
import {FlatList, Image, StyleSheet, Text, View} from 'react-native';
|
|
108
|
+
import React, {useCallback} from 'react';
|
|
109
|
+
import type {ListRenderItem} from '@react-native/virtualized-lists';
|
|
110
|
+
import {User} from '../types';
|
|
111
|
+
|
|
112
|
+
export default ({users}: { users: User[] }) => {
|
|
113
|
+
const renderItem: ListRenderItem<User> = useCallback(({item: {picture}}) => {
|
|
114
|
+
return (
|
|
115
|
+
<View style={styles.userContainer}>
|
|
116
|
+
<Image
|
|
117
|
+
source={{uri: picture.thumbnail}}
|
|
118
|
+
style={styles.userImage}
|
|
119
|
+
accessibilityLabel={'favorite-contact-avatar'}
|
|
120
|
+
/>
|
|
121
|
+
</View>
|
|
122
|
+
);
|
|
123
|
+
}, []);
|
|
124
|
+
|
|
125
|
+
if (users.length === 0) return (
|
|
126
|
+
<View style={styles.loaderContainer}>
|
|
127
|
+
<Text>Figuring out your favorites...</Text>
|
|
128
|
+
</View>
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<View style={styles.outerContainer}>
|
|
133
|
+
<Text>⭐My Favorites</Text>
|
|
134
|
+
<FlatList<User>
|
|
135
|
+
horizontal
|
|
136
|
+
showsHorizontalScrollIndicator={false}
|
|
137
|
+
data={users}
|
|
138
|
+
renderItem={renderItem}
|
|
139
|
+
keyExtractor={(item, index) => `${index}-${item.id.value}`}
|
|
140
|
+
/>
|
|
141
|
+
</View>
|
|
142
|
+
);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Looking for styles?
|
|
146
|
+
// Check examples/cookbook/app/advanced/components/FavoritesList.tsx
|
|
147
|
+
const styles =
|
|
148
|
+
...
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Our `ContactsList` component is similar to the `FavoritesList` component, but it displays the list
|
|
152
|
+
of
|
|
153
|
+
all contacts vertically.
|
|
154
|
+
|
|
155
|
+
```tsx title=network-requests/components/ContactsList.tsx
|
|
156
|
+
import { FlatList, Image, StyleSheet, Text, View } from 'react-native';
|
|
157
|
+
import React, { useCallback } from 'react';
|
|
158
|
+
import type { ListRenderItem } from '@react-native/virtualized-lists';
|
|
159
|
+
import { User } from '../types';
|
|
160
|
+
|
|
161
|
+
export default ({ users }: { users: User[] }) => {
|
|
162
|
+
const renderItem: ListRenderItem<User> = useCallback(
|
|
163
|
+
({ item: { name, email, picture, cell }, index }) => {
|
|
164
|
+
const { title, first, last } = name;
|
|
165
|
+
const backgroundColor = index % 2 === 0 ? '#f9f9f9' : '#fff';
|
|
166
|
+
return (
|
|
167
|
+
<View style={[{ backgroundColor }, styles.userContainer]}>
|
|
168
|
+
<Image source={{ uri: picture.thumbnail }} style={styles.userImage} />
|
|
169
|
+
<View>
|
|
170
|
+
<Text>
|
|
171
|
+
Name: {title} {first} {last}
|
|
172
|
+
</Text>
|
|
173
|
+
<Text>Email: {email}</Text>
|
|
174
|
+
<Text>Mobile: {cell}</Text>
|
|
175
|
+
</View>
|
|
176
|
+
</View>
|
|
177
|
+
);
|
|
178
|
+
},
|
|
179
|
+
[],
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
if (users.length === 0) return <FullScreenLoader />;
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<View>
|
|
186
|
+
<FlatList<User>
|
|
187
|
+
data={users}
|
|
188
|
+
renderItem={renderItem}
|
|
189
|
+
keyExtractor={(item, index) => `${index}-${item.id.value}`}
|
|
190
|
+
/>
|
|
191
|
+
</View>
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Looking for styles or FullScreenLoader component?
|
|
196
|
+
// Check examples/cookbook/app/advanced/components/ContactsList.tsx
|
|
197
|
+
const FullScreenLoader = () => ...
|
|
198
|
+
const styles = ...
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Start testing with a simple test
|
|
202
|
+
|
|
203
|
+
In our initial test we would like to test if the `PhoneBook` component renders the `FavoritesList`
|
|
204
|
+
and `ContactsList` components correctly.
|
|
205
|
+
We will need to mock the network requests and their corresponding responses to ensure that the component behaves as
|
|
206
|
+
expected. To mock the network requests we will use [MSW (Mock Service Worker)](https://mswjs.io/docs/getting-started).
|
|
207
|
+
|
|
208
|
+
> [!NOTE]
|
|
209
|
+
> We recommend using the Mock Service Worker (MSW) library to declaratively mock API communication in your tests instead of stubbing `fetch`, or relying on third-party adapters.
|
|
210
|
+
|
|
211
|
+
> [!INFO]
|
|
212
|
+
> You can install MSW by running `npm install msw --save-dev` or `yarn add msw --dev`.
|
|
213
|
+
> More info regarding installation can be found in [MSW's getting started guide](https://mswjs.io/docs/getting-started#step-1-install).
|
|
214
|
+
>
|
|
215
|
+
> Please make sure you're also aware of [MSW's setup guide](https://mswjs.io/docs/integrations/react-native).
|
|
216
|
+
> Please be minded that the MSW's setup guide is potentially incomplete and might contain discrepancies/missing pieces.
|
|
217
|
+
|
|
218
|
+
```tsx title=network-requests/Phonebook.test.tsx
|
|
219
|
+
import { render, screen, waitForElementToBeRemoved } from '@testing-library/react-native';
|
|
220
|
+
import React from 'react';
|
|
221
|
+
import PhoneBook from '../PhoneBook';
|
|
222
|
+
import { User } from '../types';
|
|
223
|
+
import {http, HttpResponse} from "msw";
|
|
224
|
+
import {setupServer} from "msw/node";
|
|
225
|
+
|
|
226
|
+
// Define request handlers and response resolvers for random user API.
|
|
227
|
+
// By default, we always return the happy path response.
|
|
228
|
+
const handlers = [
|
|
229
|
+
http.get('https://randomuser.me/api/*', () => {
|
|
230
|
+
return HttpResponse.json(DATA);
|
|
231
|
+
}),
|
|
232
|
+
];
|
|
233
|
+
|
|
234
|
+
// Setup a request interception server with the given request handlers.
|
|
235
|
+
const server = setupServer(...handlers);
|
|
236
|
+
|
|
237
|
+
// Enable API mocking via Mock Service Worker (MSW)
|
|
238
|
+
beforeAll(() => server.listen());
|
|
239
|
+
// Reset any runtime request handlers we may add during the tests
|
|
240
|
+
afterEach(() => server.resetHandlers());
|
|
241
|
+
// Disable API mocking after the tests are done
|
|
242
|
+
afterAll(() => server.close());
|
|
243
|
+
|
|
244
|
+
describe('PhoneBook', () => {
|
|
245
|
+
it('fetches all contacts and favorites successfully and renders lists in sections correctly', async () => {
|
|
246
|
+
await render(<PhoneBook />);
|
|
247
|
+
|
|
248
|
+
await waitForElementToBeRemoved(() => screen.getByText(/users data not quite there yet/i));
|
|
249
|
+
expect(await screen.findByText('Name: Mrs Ida Kristensen')).toBeOnTheScreen();
|
|
250
|
+
expect(await screen.findByText('Email: ida.kristensen@example.com')).toBeOnTheScreen();
|
|
251
|
+
expect(await screen.findAllByText(/name/i)).toHaveLength(3);
|
|
252
|
+
expect(await screen.findByText(/my favorites/i)).toBeOnTheScreen();
|
|
253
|
+
expect(await screen.findAllByLabelText('favorite-contact-avatar')).toHaveLength(3);
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const DATA: { results: User[] } = {
|
|
258
|
+
results: [
|
|
259
|
+
{
|
|
260
|
+
name: {
|
|
261
|
+
title: 'Mrs',
|
|
262
|
+
first: 'Ida',
|
|
263
|
+
last: 'Kristensen',
|
|
264
|
+
},
|
|
265
|
+
email: 'ida.kristensen@example.com',
|
|
266
|
+
id: {
|
|
267
|
+
name: 'CPR',
|
|
268
|
+
value: '250562-5730',
|
|
269
|
+
},
|
|
270
|
+
picture: {
|
|
271
|
+
large: 'https://randomuser.me/api/portraits/women/26.jpg',
|
|
272
|
+
medium: 'https://randomuser.me/api/portraits/med/women/26.jpg',
|
|
273
|
+
thumbnail: 'https://randomuser.me/api/portraits/thumb/women/26.jpg',
|
|
274
|
+
},
|
|
275
|
+
cell: '123-4567-890',
|
|
276
|
+
},
|
|
277
|
+
// For brevity, we have omitted the rest of the users, you can still find them in
|
|
278
|
+
// examples/cookbook/app/network-requests/__tests__/test-utils.ts
|
|
279
|
+
...
|
|
280
|
+
],
|
|
281
|
+
};
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
> [!INFO]
|
|
285
|
+
> More info regarding how to describe the network using request handlers, intercepting a request and handling its response can be found in the [MSW's documentation](https://mswjs.io/docs/getting-started#step-2-describe).
|
|
286
|
+
|
|
287
|
+
## Testing error handling
|
|
288
|
+
|
|
289
|
+
As we are dealing with network requests, and things can go wrong, we should also cover the case when
|
|
290
|
+
the API request fails. In this case, we would like to test how our application behaves when the API request fails.
|
|
291
|
+
|
|
292
|
+
> [!INFO]
|
|
293
|
+
> The nature of the network can be highly dynamic, which makes it challenging to describe it completely in a fixed list of request handlers.
|
|
294
|
+
> MSW provides us the means to override any particular network behavior using the designated `.use()` API.
|
|
295
|
+
> More info can be found in [MSW's Network behavior overrides documentation](https://mswjs.io/docs/best-practices/network-behavior-overrides)
|
|
296
|
+
|
|
297
|
+
```tsx title=network-requests/Phonebook.test.tsx
|
|
298
|
+
...
|
|
299
|
+
|
|
300
|
+
const mockServerFailureForGetAllContacts = () => {
|
|
301
|
+
server.use(
|
|
302
|
+
http.get('https://randomuser.me/api/', ({ request }) => {
|
|
303
|
+
// Construct a URL instance out of the intercepted request.
|
|
304
|
+
const url = new URL(request.url);
|
|
305
|
+
// Read the "results" URL query parameter using the "URLSearchParams" API.
|
|
306
|
+
const resultsLength = url.searchParams.get('results');
|
|
307
|
+
// Simulate a server error for the get all contacts request.
|
|
308
|
+
// We check if the "results" query parameter is set to "25"
|
|
309
|
+
// to know it's the correct request to mock, in our case get all contacts.
|
|
310
|
+
if (resultsLength === '25') {
|
|
311
|
+
return new HttpResponse(null, { status: 500 });
|
|
312
|
+
}
|
|
313
|
+
// Return the default response for all other requests that match URL and verb. (in our case get favorites)
|
|
314
|
+
return HttpResponse.json(DATA);
|
|
315
|
+
}),
|
|
316
|
+
);
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
describe('PhoneBook', () => {
|
|
320
|
+
...
|
|
321
|
+
it('fails to fetch all contacts and renders error message', async () => {
|
|
322
|
+
mockServerFailureForGetAllContacts();
|
|
323
|
+
await render(<PhoneBook />);
|
|
324
|
+
|
|
325
|
+
await waitForElementToBeRemoved(() => screen.getByText(/users data not quite there yet/i));
|
|
326
|
+
expect(
|
|
327
|
+
await screen.findByText(/an error occurred: error fetching contacts/i),
|
|
328
|
+
).toBeOnTheScreen();
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## Global guarding against unwanted API requests
|
|
335
|
+
|
|
336
|
+
As mistakes may happen, we might forget to mock a network request in one of our tests in the future.
|
|
337
|
+
To prevent us from happening, and alert when a certain network request is left unhandled, you may choose to
|
|
338
|
+
move MSW's server management from `PhoneBook.test.tsx` to Jest's setup file via [`setupFilesAfterEnv`](https://jestjs.io/docs/configuration#setupfilesafterenv-array).
|
|
339
|
+
|
|
340
|
+
```tsx title=examples/cookbook/jest-setup.ts
|
|
341
|
+
// Enable API mocking via Mock Service Worker (MSW)
|
|
342
|
+
beforeAll(() => server.listen());
|
|
343
|
+
// Reset any runtime request handlers we may add during the tests
|
|
344
|
+
afterEach(() => server.resetHandlers());
|
|
345
|
+
// Disable API mocking after the tests are done
|
|
346
|
+
afterAll(() => server.close());
|
|
347
|
+
|
|
348
|
+
// ... rest of your setup file
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
This setup will ensure you have the MSW server running before any test suite starts and stops it after all tests are done.
|
|
352
|
+
Which will result in a warning in the console if you forget to mock an API request in your test suite.
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
[MSW] Warning: intercepted a request without a matching request handler:
|
|
356
|
+
• GET https://randomuser.me/api/?results=25?results=25
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## Conclusion
|
|
360
|
+
|
|
361
|
+
Testing a component that makes network requests in combination with MSW takes some initial preparation to configure and describe the overridden networks.
|
|
362
|
+
We can achieve that by using MSW's request handlers and intercepting APIs.
|
|
363
|
+
|
|
364
|
+
Once up and running we gain full grip over the network requests, their responses, statuses.
|
|
365
|
+
Doing so is crucial to be able to test how our application behaves in different
|
|
366
|
+
scenarios, such as when the request is successful or when it fails.
|
|
367
|
+
|
|
368
|
+
When global configuration is in place, MSW's will also warn us when an unhandled network requests has occurred throughout a test suite.
|
|
369
|
+
|
|
370
|
+
## Further Reading and Alternatives
|
|
371
|
+
|
|
372
|
+
Explore more advanced scenarios for mocking network requests with MSW:
|
|
373
|
+
|
|
374
|
+
- MSW's Basics - [Intercepting requests](https://mswjs.io/docs/basics/intercepting-requests) and/or [Mocking responses](https://mswjs.io/docs/basics/mocking-responses)
|
|
375
|
+
- MSW's Network behavior - how to describe [REST](https://mswjs.io/docs/network-behavior/rest) and/or [GraphQL](https://mswjs.io/docs/network-behavior/graphql) APIs
|