Skip to content

Commit ba36a0f

Browse files
author
stevegalili
committedSep 10, 2024·
Arrange docs initially to reflect new scenario and remove jest.setTimeout
1 parent f253045 commit ba36a0f

File tree

7 files changed

+135
-312
lines changed

7 files changed

+135
-312
lines changed
 

‎examples/cookbook/app/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,5 @@ type Recipe = {
8484
const recipes: Recipe[] = [
8585
{ id: 1, title: 'Welcome Screen with Custom Render', path: 'custom-render/' },
8686
{ id: 2, title: 'Task List with Jotai', path: 'state-management/jotai/' },
87-
{ id: 3, title: 'Phone book with\na Variety of Net. Req. Methods', path: 'network-requests/' },
87+
{ id: 3, title: 'Phone book with\na Variety of Net. Req. Methods', path: 'advanced/' },
8888
];

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

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

9-
jest.setTimeout(10000);
10-
119
describe('PhoneBook', () => {
1210
it('fetches all contacts and favorites successfully and renders lists in sections correctly', async () => {
1311
render(<PhoneBook />);

‎website/docs/12.x/cookbook/_meta.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
},
88
{
99
"type": "dir",
10-
"name": "network-requests",
11-
"label": "Network Requests Recipes"
10+
"name": "advanced",
11+
"label": "Advanced Recipes"
1212
},
1313
{
1414
"type": "dir",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
["network-requests"]

‎website/docs/12.x/cookbook/network-requests/axios.md renamed to ‎website/docs/12.x/cookbook/advanced/network-requests.md

Lines changed: 131 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,58 @@
1-
# Axios
1+
# Network Requests
22

33
## Introduction
44

5-
Axios is a popular library for making HTTP requests in JavaScript. It is promise-based and has a
6-
simple API that makes it easy to use.
7-
In this guide, we will show you how to mock Axios requests and guard your test suits from unwanted
8-
and unmocked API requests.
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
912

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

1418
## Phonebook Example
1519

16-
Let's assume we have a simple phonebook application that uses Axios for fetching Data from a server.
17-
In our case, we have a list of favorite contacts that we want to display in our application.
20+
Let's assume we have a simple phonebook application that
21+
uses [`fetch`](https://reactnative.dev/docs/network#using-fetch) for fetching Data from a server.
22+
In our case, we have a list of contacts and favorites that we want to display in our application.
1823

1924
This is how the root of the application looks like:
2025

2126
```tsx title=network-requests/Phonebook.tsx
22-
import React, {useEffect, useState} from 'react';
23-
import {User} from './types';
27+
import React, { useEffect, useState } from 'react';
28+
import { Text } from 'react-native';
29+
import { User } from './types';
30+
import ContactsList from './components/ContactsList';
2431
import FavoritesList from './components/FavoritesList';
32+
import getAllContacts from './api/getAllContacts';
33+
import getAllFavorites from './api/getAllFavorites';
2534

2635
export default () => {
36+
const [usersData, setUsersData] = useState<User[]>([]);
2737
const [favoritesData, setFavoritesData] = useState<User[]>([]);
2838
const [error, setError] = useState<string | null>(null);
2939

3040
useEffect(() => {
41+
const _getAllContacts = async () => {
42+
const _data = await getAllContacts();
43+
setUsersData(_data);
44+
};
45+
const _getAllFavorites = async () => {
46+
const _data = await getAllFavorites();
47+
setFavoritesData(_data);
48+
};
49+
3150
const run = async () => {
3251
try {
33-
const _data = await getAllFavorites();
34-
setFavoritesData(_data);
52+
await Promise.all([_getAllContacts(), _getAllFavorites()]);
3553
} catch (e) {
36-
setError(e.message);
54+
const message = isErrorWithMessage(e) ? e.message : 'Something went wrong';
55+
setError(message);
3756
}
3857
};
3958

@@ -45,27 +64,46 @@ export default () => {
4564
}
4665

4766
return (
48-
<FavoritesList users={favoritesData}/>
67+
<>
68+
<FavoritesList users={favoritesData} />
69+
<ContactsList users={usersData} />
70+
</>
4971
);
5072
};
73+
```
5174

75+
We fetch the contacts from the server using the `getAllFavorites` function that utilizes `fetch`.
76+
77+
```tsx title=network-requests/api/getAllContacts.ts
78+
import { User } from '../types';
79+
80+
export default async (): Promise<User[]> => {
81+
const res = await fetch('https://randomuser.me/api/?results=25');
82+
if (!res.ok) {
83+
throw new Error(`Error fetching contacts`);
84+
}
85+
const json = await res.json();
86+
return json.results;
87+
};
5288
```
5389

54-
We fetch the contacts from the server using the `getAllFavorites` function that utilizes Axios.
90+
We do the same for fetching the favorites, but this time limiting the results to 10.
5591

5692
```tsx title=network-requests/api/getAllFavorites.ts
57-
import axios from 'axios';
58-
import {User} from '../types';
93+
import { User } from '../types';
5994

6095
export default async (): Promise<User[]> => {
61-
const res = await axios.get('https://randomuser.me/api/?results=10');
62-
return res.data.results;
96+
const res = await fetch('https://randomuser.me/api/?results=10');
97+
if (!res.ok) {
98+
throw new Error(`Error fetching favorites`);
99+
}
100+
const json = await res.json();
101+
return json.results;
63102
};
64-
65103
```
66104

67105
Our `FavoritesList` component is a simple component that displays the list of favorite contacts and
68-
their avatars.
106+
their avatars horizontally.
69107

70108
```tsx title=network-requests/components/FavoritesList.tsx
71109
import {FlatList, Image, StyleSheet, Text, View} from 'react-native';
@@ -107,19 +145,73 @@ export default ({users}: { users: User[] }) => {
107145
};
108146

109147
// Looking for styles?
110-
// Check examples/cookbook/app/network-requests/components/FavoritesList.tsx
148+
// Check examples/cookbook/app/advanced/components/FavoritesList.tsx
111149
const styles =
112150
...
113151
```
114152

153+
Our `ContactsList` component is similar to the `FavoritesList` component, but it displays the list
154+
of
155+
all contacts vertically.
156+
157+
```tsx title=network-requests/components/ContactsList.tsx
158+
import { FlatList, Image, StyleSheet, Text, View } from 'react-native';
159+
import React, { useCallback } from 'react';
160+
import type { ListRenderItem } from '@react-native/virtualized-lists';
161+
import { User } from '../types';
162+
163+
export default ({ users }: { users: User[] }) => {
164+
const renderItem: ListRenderItem<User> = useCallback(
165+
({ item: { name, email, picture, cell }, index }) => {
166+
const { title, first, last } = name;
167+
const backgroundColor = index % 2 === 0 ? '#f9f9f9' : '#fff';
168+
return (
169+
<View style={[{ backgroundColor }, styles.userContainer]}>
170+
<Image source={{ uri: picture.thumbnail }} style={styles.userImage} />
171+
<View>
172+
<Text>
173+
Name: {title} {first} {last}
174+
</Text>
175+
<Text>Email: {email}</Text>
176+
<Text>Mobile: {cell}</Text>
177+
</View>
178+
</View>
179+
);
180+
},
181+
[],
182+
);
183+
184+
if (users.length === 0) return <FullScreenLoader />;
185+
186+
return (
187+
<View>
188+
<FlatList<User>
189+
data={users}
190+
renderItem={renderItem}
191+
keyExtractor={(item, index) => `${index}-${item.id.value}`}
192+
/>
193+
</View>
194+
);
195+
};
196+
197+
// Looking for styles or FullScreenLoader component?
198+
// Check examples/cookbook/app/advanced/components/ContactsList.tsx
199+
const FullScreenLoader = () => ...
200+
const styles = ...
201+
```
202+
115203
## Start testing with a simple test
116-
In our test we will make sure we mock the `axios.get` function to return the data we want.
117-
In this specific case, we will return a list of 3 users.
204+
205+
In our test we would like to test if the `PhoneBook` component renders the `FavoritesList`
206+
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.
209+
210+
```tsx title=network-requests/Phonebook.test.tsx
118211

119212
:::info
120-
To prevent unexpected behavior, we ensure the following:
121-
- Prevent the mock from resolving data multiple times by using `mockResolvedValueOnce`.
122-
- Ensure the URL matches the base URL of the API by using a custom function `ensureUrlMatchesBaseUrl`.
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.
123215
124216
:::
125217
@@ -179,20 +271,23 @@ const DATA: { results: User[] } = {
179271
cell: '123-4567-890',
180272
},
181273
// For brevity, we have omitted the rest of the users, you can still find them in
182-
// examples/cookbook/app/network-requests/__tests__/PhoneBook.test.tsx
274+
// examples/cookbook/app/advanced/__tests__/PhoneBook.test.tsx
183275
...
184276
],
185277
};
186278

187279
```
188280

189281
## Testing error handling
190-
As we are dealing with network requests, we should also test how our application behaves when the API
282+
283+
As we are dealing with network requests, we should also test how our application behaves when the
284+
API
191285
request fails. We can mock the `axios.get` function to throw an error and test if our application is
192286
handling the error correctly.
193287

194288
:::note
195-
It is good to note that Axios throws auto. an error when the response status code is not in the range of 2xx.
289+
It is good to note that Axios throws auto. an error when the response status code is not in the
290+
range of 2xx.
196291
:::
197292

198293
```tsx title=network-requests/Phonebook.test.tsx
@@ -242,6 +337,7 @@ export default {
242337
```
243338

244339
## Conclusion
340+
245341
Testing a component that makes network requests with Axios is straightforward. By mocking the Axios
246342
requests, we can control the data that is returned and test how our application behaves in different
247343
scenarios, such as when the request is successful or when it fails.
@@ -252,5 +348,8 @@ and how to guard against unwanted API requests throughout your test suite.
252348
## Further Reading and Alternatives
253349

254350
Explore more powerful tools for mocking network requests in your React Native application:
255-
- [Axios Mock Adapter](https://github.com/ctimmerm/axios-mock-adapter): A popular library for mocking Axios calls with an extensive API, making it easy to simulate various scenarios.
256-
- [MSW (Mock Service Worker)](https://mswjs.io/): Great for spinning up a local test server that intercepts network requests at the network level, providing end-to-end testing capabilities.
351+
352+
- [Axios Mock Adapter](https://github.com/ctimmerm/axios-mock-adapter): A popular library for
353+
mocking Axios calls with an extensive API, making it easy to simulate various scenarios.
354+
- [MSW (Mock Service Worker)](https://mswjs.io/): Great for spinning up a local test server that
355+
intercepts network requests at the network level, providing end-to-end testing capabilities.

‎website/docs/12.x/cookbook/network-requests/_meta.json

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)