Skip to content

Commit 5d55fcd

Browse files
author
stevegalili
committed
updating docs (1)
1 parent ba36a0f commit 5d55fcd

File tree

2 files changed

+82
-57
lines changed

2 files changed

+82
-57
lines changed

examples/cookbook/app/network-requests/__tests__/PhoneBook.test.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
mockServerFailureForGetAllFavorites,
77
} from './test-utils';
88

9+
jest.setTimeout(10000);
10+
911
describe('PhoneBook', () => {
1012
it('fetches all contacts and favorites successfully and renders lists in sections correctly', async () => {
1113
render(<PhoneBook />);
@@ -23,7 +25,9 @@ describe('PhoneBook', () => {
2325
render(<PhoneBook />);
2426

2527
await waitForElementToBeRemoved(() => screen.getByText(/users data not quite there yet/i));
26-
expect(await screen.findByText(/error fetching contacts/i)).toBeOnTheScreen();
28+
expect(
29+
await screen.findByText(/an error occurred: error fetching contacts/i),
30+
).toBeOnTheScreen();
2731
});
2832

2933
it('fails to fetch favorites and renders error message', async () => {

website/docs/12.x/cookbook/advanced/network-requests.md

Lines changed: 77 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ In this guide, we will show you how to mock network requests and guard your test
1111
and unmocked/unhandled network requests
1212

1313
:::info
14-
To simulate a real-world scenario, we will use
15-
the [Random User Generator API](https://randomuser.me/) that provides random user data.
14+
To simulate a real-world scenario, we will use the [Random User Generator API](https://randomuser.me/) that provides random user data.
1615
:::
1716

1817
## Phonebook Example
@@ -87,7 +86,7 @@ export default async (): Promise<User[]> => {
8786
};
8887
```
8988

90-
We do the same for fetching the favorites, but this time limiting the results to 10.
89+
We have similar function for fetching the favorites, but this time limiting the results to 10.
9190

9291
```tsx title=network-requests/api/getAllFavorites.ts
9392
import { User } from '../types';
@@ -202,53 +201,60 @@ const styles = ...
202201

203202
## Start testing with a simple test
204203

205-
In our test we would like to test if the `PhoneBook` component renders the `FavoritesList`
204+
In our initial test we would like to test if the `PhoneBook` component renders the `FavoritesList`
206205
and `ContactsList` components correctly.
207-
We will mock the network requests and their responses to ensure that the component behaves as
208-
expected. We will use [MSW (Mock Service Worker)](https://mswjs.io/docs/getting-started) to mock the network requests.
206+
We will need to mock the network requests and their corresponding responses to ensure that the component behaves as
207+
expected. To mock the network requests we will use [MSW (Mock Service Worker)](https://mswjs.io/docs/getting-started).
209208

210-
```tsx title=network-requests/Phonebook.test.tsx
209+
:::note
210+
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.
211+
:::
211212

212213
:::info
213-
We recommend using the Mock Service Worker (MSW) library to declaratively mock API communication in
214-
your tests instead of stubbing `fetch, or relying on third-party adapters.
214+
You can install MSW by running `npm install msw --save-dev` or `yarn add msw --dev`.
215+
More info regarding installation can be found in [MSW's getting started guide](https://mswjs.io/docs/getting-started#step-1-install).
215216

217+
Please make sure you're also aware of [MSW's setup guide](https://mswjs.io/docs/integrations/react-native).
218+
Please be minded that the MSW's setup guide is potentially incomplete and might contain discrepancies/missing pieces.
216219
:::
217220

218221
```tsx title=network-requests/Phonebook.test.tsx
219-
import {render, waitForElementToBeRemoved} from '@testing-library/react-native';
222+
import { render, screen, waitForElementToBeRemoved } from '@testing-library/react-native';
220223
import React from 'react';
221224
import PhoneBook from '../PhoneBook';
222-
import {User} from '../types';
223-
import axios from 'axios';
224-
import {MismatchedUrlError} from './test-utils';
225-
226-
const ensureUrlMatchesBaseUrl = (url: string) => {
227-
if (!url.includes('https://randomuser.me/api')) throw new MismatchedUrlError(url);
228-
};
229-
230-
export const mockAxiosGetWithSuccessResponse = () => {
231-
(axios.get as jest.Mock).mockImplementationOnce((url) => {
232-
// Ensure the URL matches the base URL of the API
233-
ensureUrlMatchesBaseUrl(url);
234-
235-
return Promise.resolve({ data: DATA });
236-
});
237-
};
225+
import { User } from '../types';
226+
import {http, HttpResponse} from "msw";
227+
import {setupServer} from "msw/node";
228+
229+
// Define request handlers and response resolvers for random user API.
230+
// By default, we always return the happy path response.
231+
const handlers = [
232+
http.get('https://randomuser.me/api/*', () => {
233+
return HttpResponse.json(DATA);
234+
}),
235+
];
236+
237+
// Setup a request interception server with the given request handlers.
238+
const server = setupServer(...handlers);
239+
240+
// Enable API mocking via Mock Service Worker (MSW)
241+
beforeAll(() => server.listen());
242+
// Reset any runtime request handlers we may add during the tests
243+
afterEach(() => server.resetHandlers());
244+
// Disable API mocking after the tests are done
245+
afterAll(() => server.close());
238246

239247
describe('PhoneBook', () => {
240-
it('fetches favorites successfully and renders all users avatars', async () => {
241-
// Mock the axios.get function to return the data we want
242-
mockAxiosGetWithSuccessResponse();
243-
render(<PhoneBook/>);
248+
it('fetches all contacts and favorites successfully and renders lists in sections correctly', async () => {
249+
render(<PhoneBook />);
244250

245-
// Wait for the loader to disappear
246-
await waitForElementToBeRemoved(() => screen.getByText(/figuring out your favorites/i));
251+
await waitForElementToBeRemoved(() => screen.getByText(/users data not quite there yet/i));
252+
expect(await screen.findByText('Name: Mrs Ida Kristensen')).toBeOnTheScreen();
253+
expect(await screen.findByText('Email: [email protected]')).toBeOnTheScreen();
254+
expect(await screen.findAllByText(/name/i)).toHaveLength(3);
247255
expect(await screen.findByText(/my favorites/i)).toBeOnTheScreen();
248-
// All the avatars should be rendered
249256
expect(await screen.findAllByLabelText('favorite-contact-avatar')).toHaveLength(3);
250257
});
251-
});
252258

253259
const DATA: { results: User[] } = {
254260
results: [
@@ -271,46 +277,61 @@ const DATA: { results: User[] } = {
271277
cell: '123-4567-890',
272278
},
273279
// For brevity, we have omitted the rest of the users, you can still find them in
274-
// examples/cookbook/app/advanced/__tests__/PhoneBook.test.tsx
280+
// examples/cookbook/app/network-requests/__tests__/test-utils.ts
275281
...
276282
],
277283
};
278-
279284
```
280285
286+
:::info
287+
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).
288+
:::
289+
281290
## Testing error handling
282291
283-
As we are dealing with network requests, we should also test how our application behaves when the
284-
API
285-
request fails. We can mock the `axios.get` function to throw an error and test if our application is
286-
handling the error correctly.
292+
As we are dealing with network requests, and things can go wrong, we should also cover the case when
293+
the API request fails. In this case, we would like to test how our application behaves when the API request fails.
287294
288-
:::note
295+
:::info
289296
It is good to note that Axios throws auto. an error when the response status code is not in the
290297
range of 2xx.
291298
:::
292299
293300
```tsx title=network-requests/Phonebook.test.tsx
294301
...
295302

296-
export const mockAxiosGetWithFailureResponse = () => {
297-
(axios.get as jest.Mock).mockImplementationOnce((url) => {
298-
ensureUrlMatchesBaseUrl(url);
299-
300-
return Promise.reject({ message: 'Error fetching favorites' });
301-
});
303+
const mockServerFailureForGetAllContacts = () => {
304+
server.use(
305+
http.get('https://randomuser.me/api/', ({ request }) => {
306+
// Construct a URL instance out of the intercepted request.
307+
const url = new URL(request.url);
308+
// Read the "results" URL query parameter using the "URLSearchParams" API.
309+
const resultsLength = url.searchParams.get('results');
310+
// Simulate a server error for the get all contacts request.
311+
// We check if the "results" query parameter is set to "25"
312+
// to know it's the correct request to mock, in our case get all contacts.
313+
if (resultsLength === '25') {
314+
return new HttpResponse(null, { status: 500 });
315+
}
316+
// Return the default response for all other requests that match URL and verb. (in our case get favorites)
317+
return HttpResponse.json(DATA);
318+
}),
319+
);
302320
};
303321

304-
it('fails to fetch favorites and renders error message', async () => {
305-
// Mock the axios.get function to throw an error
306-
mockAxiosGetWithFailureResponse();
307-
render(<PhoneBook />);
308-
309-
// Wait for the loader to disappear
310-
await waitForElementToBeRemoved(() => screen.getByText(/figuring out your favorites/i));
311-
// Error message should be displayed
312-
expect(await screen.findByText(/error fetching favorites/i)).toBeOnTheScreen();
322+
describe('PhoneBook', () => {
323+
...
324+
it('fails to fetch all contacts and renders error message', async () => {
325+
mockServerFailureForGetAllContacts();
326+
render(<PhoneBook />);
327+
328+
await waitForElementToBeRemoved(() => screen.getByText(/users data not quite there yet/i));
329+
expect(
330+
await screen.findByText(/an error occurred: error fetching contacts/i),
331+
).toBeOnTheScreen();
332+
});
313333
});
334+
314335
````
315336

316337
## Global guarding against unwanted API requests

0 commit comments

Comments
 (0)