0
# Error Handling
1
2
Error suppression and handling utilities for testing error scenarios and managing console output during tests. These utilities help create clean test environments and properly test error conditions in hooks.
3
4
## Capabilities
5
6
### suppressErrorOutput Function
7
8
Temporarily suppresses React error boundary console output, useful for testing error scenarios without cluttering test output. Returns a function to restore normal error output.
9
10
```typescript { .api }
11
/**
12
* Suppress React error boundary console output
13
* @returns Function to restore normal error output
14
*/
15
function suppressErrorOutput(): () => void;
16
```
17
18
**Usage Examples:**
19
20
```typescript
21
import { renderHook, suppressErrorOutput } from "@testing-library/react-hooks";
22
import { useState } from "react";
23
24
function useErrorHook(shouldError: boolean) {
25
const [count, setCount] = useState(0);
26
27
if (shouldError) {
28
throw new Error("Hook error occurred");
29
}
30
31
return { count, setCount };
32
}
33
34
test("testing hook errors with suppressed output", () => {
35
// Suppress error output to keep test logs clean
36
const restoreConsole = suppressErrorOutput();
37
38
try {
39
const { result } = renderHook(() => useErrorHook(true));
40
41
// This will throw, but without console noise
42
expect(result.error).toBeInstanceOf(Error);
43
expect(result.error.message).toBe("Hook error occurred");
44
} finally {
45
// Always restore console output
46
restoreConsole();
47
}
48
});
49
50
// Using suppressErrorOutput with async tests
51
test("async error handling", async () => {
52
const restoreConsole = suppressErrorOutput();
53
54
function useAsyncError() {
55
const [error, setError] = useState(null);
56
57
const triggerError = async () => {
58
await new Promise(resolve => setTimeout(resolve, 100));
59
throw new Error("Async error");
60
};
61
62
return { error, triggerError };
63
}
64
65
try {
66
const { result } = renderHook(() => useAsyncError());
67
68
await expect(result.current.triggerError()).rejects.toThrow("Async error");
69
} finally {
70
restoreConsole();
71
}
72
});
73
```
74
75
### Error Boundary Testing
76
77
Testing hooks that throw errors requires understanding how React's error boundaries work with the testing library:
78
79
```typescript
80
function useValidatedState(initialValue: string, validator: (value: string) => boolean) {
81
const [value, setValue] = useState(initialValue);
82
83
const updateValue = (newValue: string) => {
84
if (!validator(newValue)) {
85
throw new Error(`Invalid value: ${newValue}`);
86
}
87
setValue(newValue);
88
};
89
90
return { value, updateValue };
91
}
92
93
test("error boundary handling", () => {
94
const restoreConsole = suppressErrorOutput();
95
96
try {
97
const { result } = renderHook(() =>
98
useValidatedState("valid", (value) => value !== "invalid")
99
);
100
101
expect(result.current.value).toBe("valid");
102
103
// This should throw and be caught by error boundary
104
act(() => {
105
expect(() => {
106
result.current.updateValue("invalid");
107
}).toThrow("Invalid value: invalid");
108
});
109
110
// Check error state
111
expect(result.error).toBeInstanceOf(Error);
112
expect(result.error.message).toBe("Invalid value: invalid");
113
} finally {
114
restoreConsole();
115
}
116
});
117
```
118
119
### Global Error Filtering
120
121
The library provides a global configuration to disable error filtering entirely:
122
123
```javascript
124
// Import at the top of test file or in setup
125
import "@testing-library/react-hooks/disable-error-filtering";
126
127
// Or require in CommonJS
128
require("@testing-library/react-hooks/disable-error-filtering");
129
```
130
131
**Usage:**
132
133
```typescript
134
// After importing disable-error-filtering
135
import { renderHook } from "@testing-library/react-hooks";
136
137
// All console errors will be shown, even from error boundaries
138
test("with error filtering disabled", () => {
139
function useThrowingHook() {
140
throw new Error("This error will be shown in console");
141
}
142
143
const { result } = renderHook(() => useThrowingHook());
144
145
expect(result.error).toBeInstanceOf(Error);
146
// Error will appear in console output
147
});
148
```
149
150
### Error Recovery Testing
151
152
Testing hooks that can recover from errors:
153
154
```typescript
155
function useErrorRecovery() {
156
const [error, setError] = useState(null);
157
const [retryCount, setRetryCount] = useState(0);
158
const [data, setData] = useState(null);
159
160
const fetchData = async () => {
161
try {
162
setError(null);
163
164
if (retryCount < 2) {
165
setRetryCount(prev => prev + 1);
166
throw new Error("Temporary failure");
167
}
168
169
setData("Success data");
170
} catch (err) {
171
setError(err);
172
}
173
};
174
175
const retry = () => {
176
fetchData();
177
};
178
179
useEffect(() => {
180
fetchData();
181
}, []);
182
183
return { data, error, retryCount, retry };
184
}
185
186
test("error recovery mechanism", async () => {
187
const { result, waitFor } = renderHook(() => useErrorRecovery());
188
189
// Initially should have error
190
await waitFor(() => result.current.error !== null);
191
192
expect(result.current.error.message).toBe("Temporary failure");
193
expect(result.current.retryCount).toBe(1);
194
expect(result.current.data).toBe(null);
195
196
// Retry should fail again
197
act(() => {
198
result.current.retry();
199
});
200
201
await waitFor(() => result.current.retryCount === 2);
202
203
expect(result.current.error.message).toBe("Temporary failure");
204
expect(result.current.data).toBe(null);
205
206
// Third retry should succeed
207
act(() => {
208
result.current.retry();
209
});
210
211
await waitFor(() => result.current.data !== null);
212
213
expect(result.current.error).toBe(null);
214
expect(result.current.data).toBe("Success data");
215
});
216
```
217
218
### Error Context Testing
219
220
Testing hooks that provide error contexts:
221
222
```typescript
223
const ErrorContext = React.createContext({
224
error: null,
225
clearError: () => {},
226
reportError: (error) => {}
227
});
228
229
function useErrorContext() {
230
return React.useContext(ErrorContext);
231
}
232
233
function useWithErrorReporting(operation: () => void) {
234
const { reportError } = useErrorContext();
235
236
const executeOperation = () => {
237
try {
238
operation();
239
} catch (error) {
240
reportError(error);
241
}
242
};
243
244
return { executeOperation };
245
}
246
247
test("error context integration", () => {
248
const mockReportError = jest.fn();
249
const mockClearError = jest.fn();
250
251
const wrapper = ({ children }) => (
252
<ErrorContext.Provider value={{
253
error: null,
254
clearError: mockClearError,
255
reportError: mockReportError
256
}}>
257
{children}
258
</ErrorContext.Provider>
259
);
260
261
const { result } = renderHook(() =>
262
useWithErrorReporting(() => {
263
throw new Error("Operation failed");
264
}),
265
{ wrapper }
266
);
267
268
act(() => {
269
result.current.executeOperation();
270
});
271
272
expect(mockReportError).toHaveBeenCalledWith(
273
expect.objectContaining({ message: "Operation failed" })
274
);
275
});
276
```
277
278
### Best Practices for Error Testing
279
280
**Always Restore Console:**
281
```typescript
282
test("proper console restoration", () => {
283
const restoreConsole = suppressErrorOutput();
284
285
try {
286
// Test error scenarios
287
const { result } = renderHook(() => useErrorHook());
288
expect(result.error).toBeDefined();
289
} finally {
290
// Always restore, even if test fails
291
restoreConsole();
292
}
293
});
294
```
295
296
**Use Test Helpers:**
297
```typescript
298
// Helper function for error testing
299
function testHookError(hookFn, expectedError) {
300
const restoreConsole = suppressErrorOutput();
301
302
try {
303
const { result } = renderHook(hookFn);
304
expect(result.error).toBeInstanceOf(Error);
305
expect(result.error.message).toBe(expectedError);
306
} finally {
307
restoreConsole();
308
}
309
}
310
311
test("using error test helper", () => {
312
testHookError(
313
() => useValidatedState("", () => false),
314
"Invalid value: "
315
);
316
});
317
```
318
319
**Error Boundaries with Cleanup:**
320
```typescript
321
test("error boundaries with cleanup", () => {
322
const restoreConsole = suppressErrorOutput();
323
const mockCleanup = jest.fn();
324
325
addCleanup(mockCleanup);
326
327
try {
328
const { result } = renderHook(() => useErrorHook(true));
329
expect(result.error).toBeDefined();
330
} finally {
331
restoreConsole();
332
}
333
334
// Cleanup should still run despite error
335
expect(mockCleanup).toHaveBeenCalled();
336
});
337
```