0
# React Hooks Testing Library
1
2
React Hooks Testing Library provides simple and complete React hooks testing utilities that encourage good testing practices. It allows you to create a simple test harness for React hooks that handles running them within the body of a function component, providing various utility functions for updating inputs and retrieving outputs of custom hooks.
3
4
## Package Information
5
6
- **Package Name**: react-hooks-testing-library
7
- **Package Type**: npm
8
- **Language**: JavaScript with TypeScript definitions
9
- **Installation**: `npm install --save-dev react-hooks-testing-library`
10
11
**Dependencies:**
12
- `react` (peer dependency, ^16.8.0)
13
- `react-test-renderer` (peer dependency, ^16.8.0)
14
15
## Core Imports
16
17
```javascript
18
import { renderHook, act } from "react-hooks-testing-library";
19
// testHook is also available but deprecated - use renderHook instead
20
```
21
22
For CommonJS:
23
24
```javascript
25
const { renderHook, act } = require("react-hooks-testing-library");
26
// testHook is also available but deprecated - use renderHook instead
27
```
28
29
## Basic Usage
30
31
```javascript
32
import { renderHook, act } from "react-hooks-testing-library";
33
import { useState, useCallback } from "react";
34
35
// Example custom hook
36
function useCounter() {
37
const [count, setCount] = useState(0);
38
const increment = useCallback(() => setCount((x) => x + 1), []);
39
const decrement = useCallback(() => setCount((x) => x - 1), []);
40
return { count, increment, decrement };
41
}
42
43
// Test the hook
44
test("should increment counter", () => {
45
const { result } = renderHook(() => useCounter());
46
47
act(() => result.current.increment());
48
49
expect(result.current.count).toBe(1);
50
});
51
52
// Test with async operations
53
test("should handle async updates", async () => {
54
const { result, waitForNextUpdate } = renderHook(() => useAsyncHook());
55
56
expect(result.current.loading).toBe(true);
57
58
await waitForNextUpdate();
59
60
expect(result.current.loading).toBe(false);
61
});
62
```
63
64
## Capabilities
65
66
### Hook Rendering
67
68
Renders a test component that calls the provided hook callback every time it renders.
69
70
```typescript { .api }
71
/**
72
* Renders a test component that calls the provided callback, including any hooks it calls
73
* @param callback - Function to call each render that should call one or more hooks for testing
74
* @param options - Optional configuration object
75
* @returns Object with result, utilities for testing async hooks, rerendering, and unmounting
76
*/
77
function renderHook<P, R>(
78
callback: (props: P) => R,
79
options?: {
80
initialProps?: P;
81
wrapper?: React.ComponentType;
82
}
83
): {
84
readonly result: {
85
readonly current: R;
86
readonly error: Error;
87
};
88
readonly waitForNextUpdate: () => Promise<void>;
89
readonly unmount: () => boolean;
90
readonly rerender: (hookProps?: P) => void;
91
};
92
```
93
94
**Usage Examples:**
95
96
```javascript
97
// Basic hook testing
98
const { result } = renderHook(() => useState(0));
99
expect(result.current[0]).toBe(0);
100
101
// Testing hooks with props
102
const { result, rerender } = renderHook(
103
({ initialCount }) => useState(initialCount),
104
{ initialProps: { initialCount: 5 } }
105
);
106
expect(result.current[0]).toBe(5);
107
108
// Rerender with new props
109
rerender({ initialCount: 10 });
110
expect(result.current[0]).toBe(5); // State is preserved, initialCount only used on first render
111
112
// Testing hooks that require context
113
const wrapper = ({ children }) => (
114
<ThemeProvider theme="dark">{children}</ThemeProvider>
115
);
116
const { result } = renderHook(() => useTheme(), { wrapper });
117
expect(result.current.theme).toBe("dark");
118
```
119
120
### State Updates and Effects
121
122
Wrapper around react-test-renderer's act function for handling state updates and effects.
123
124
```typescript { .api }
125
/**
126
* Ensures that updates related to state changes, effects, and event handlers are properly flushed
127
* @param callback - Function to execute within act scope
128
* @returns void
129
*/
130
function act(callback: () => void): void;
131
```
132
133
**Usage Examples:**
134
135
```javascript
136
// Wrapping state updates
137
const { result } = renderHook(() => useCounter());
138
139
act(() => {
140
result.current.increment();
141
});
142
143
expect(result.current.count).toBe(1);
144
145
// Multiple state updates
146
act(() => {
147
result.current.increment();
148
result.current.increment();
149
});
150
151
expect(result.current.count).toBe(3);
152
```
153
154
### Async Hook Testing
155
156
Handle asynchronous operations in hooks with proper waiting mechanisms.
157
158
```typescript { .api }
159
/**
160
* Returns a Promise that resolves the next time the hook renders
161
* Commonly used when state is updated as the result of an asynchronous action
162
* @returns Promise that resolves on next hook render
163
*/
164
waitForNextUpdate(): Promise<void>;
165
```
166
167
**Usage Examples:**
168
169
```javascript
170
// Testing async hooks
171
const useAsyncData = () => {
172
const [data, setData] = useState(null);
173
const [loading, setLoading] = useState(true);
174
175
useEffect(() => {
176
fetchData().then((result) => {
177
setData(result);
178
setLoading(false);
179
});
180
}, []);
181
182
return { data, loading };
183
};
184
185
test("should load data asynchronously", async () => {
186
const { result, waitForNextUpdate } = renderHook(() => useAsyncData());
187
188
expect(result.current.loading).toBe(true);
189
expect(result.current.data).toBe(null);
190
191
await waitForNextUpdate();
192
193
expect(result.current.loading).toBe(false);
194
expect(result.current.data).toBeDefined();
195
});
196
```
197
198
### Hook Re-rendering
199
200
Re-render the hook with new props to test how it responds to prop changes.
201
202
```typescript { .api }
203
/**
204
* Function to rerender the test component including any hooks called in the callback function
205
* @param newProps - New props to pass to the callback function for future renders
206
* @returns void
207
*/
208
rerender(newProps?: P): void;
209
```
210
211
**Usage Examples:**
212
213
```javascript
214
// Testing prop changes
215
const useGreeting = (name) => {
216
return `Hello, ${name}!`;
217
};
218
219
const { result, rerender } = renderHook(
220
({ name }) => useGreeting(name),
221
{ initialProps: { name: "Alice" } }
222
);
223
224
expect(result.current).toBe("Hello, Alice!");
225
226
rerender({ name: "Bob" });
227
expect(result.current).toBe("Hello, Bob!");
228
```
229
230
### Hook Cleanup
231
232
Unmount the test component to trigger cleanup effects for useEffect hooks.
233
234
```typescript { .api }
235
/**
236
* Function to unmount the test component
237
* Commonly used to trigger cleanup effects for useEffect hooks
238
* @returns boolean indicating if unmount was successful
239
*/
240
unmount(): boolean;
241
```
242
243
**Usage Examples:**
244
245
```javascript
246
// Testing cleanup effects
247
const useTimer = () => {
248
const [count, setCount] = useState(0);
249
250
useEffect(() => {
251
const timer = setInterval(() => {
252
setCount((c) => c + 1);
253
}, 1000);
254
255
return () => clearInterval(timer);
256
}, []);
257
258
return count;
259
};
260
261
test("should cleanup timer on unmount", () => {
262
const { unmount } = renderHook(() => useTimer());
263
264
// Verify cleanup doesn't throw errors
265
expect(() => unmount()).not.toThrow();
266
});
267
```
268
269
### Error Handling
270
271
Access errors thrown during hook execution for testing error scenarios.
272
273
```typescript { .api }
274
/**
275
* Access to any error thrown during hook execution
276
*/
277
interface HookResult<R> {
278
readonly result: {
279
readonly current: R;
280
readonly error: Error;
281
};
282
}
283
```
284
285
**Usage Examples:**
286
287
```javascript
288
// Testing hook errors
289
const useErrorProneHook = (shouldThrow) => {
290
if (shouldThrow) {
291
throw new Error("Something went wrong");
292
}
293
return "success";
294
};
295
296
test("should capture hook errors", () => {
297
const { result } = renderHook(
298
({ shouldThrow }) => useErrorProneHook(shouldThrow),
299
{ initialProps: { shouldThrow: true } }
300
);
301
302
expect(result.error).toBeInstanceOf(Error);
303
expect(result.error.message).toBe("Something went wrong");
304
});
305
```
306
307
## Types
308
309
```typescript { .api }
310
/**
311
* Options for configuring hook rendering
312
*/
313
interface RenderHookOptions<P> {
314
/** Initial props passed to the hook callback */
315
initialProps?: P;
316
/** React component to wrap around the rendered hook */
317
wrapper?: React.ComponentType;
318
}
319
320
/**
321
* Result object returned by renderHook
322
*/
323
interface RenderHookResult<P, R> {
324
/** Container for hook return value and any errors */
325
readonly result: {
326
/** Current return value of the hook callback */
327
readonly current: R;
328
/** Error thrown during hook execution, if any */
329
readonly error: Error;
330
};
331
/** Promise that resolves on next hook render */
332
readonly waitForNextUpdate: () => Promise<void>;
333
/** Function to unmount the test component */
334
readonly unmount: () => boolean;
335
/** Function to rerender with optional new props */
336
readonly rerender: (hookProps?: P) => void;
337
}
338
```
339
340
## Deprecated APIs
341
342
### testHook (Deprecated)
343
344
```typescript { .api }
345
/**
346
* @deprecated Use renderHook instead. Will be removed in a future version.
347
* Legacy alias for renderHook function
348
*/
349
const testHook: typeof renderHook;
350
```
351
352
The `testHook` function is deprecated and shows a console warning. Use `renderHook` instead for all new code.