2
2
id : example-react-hooks
3
3
title : React Hooks
4
4
---
5
-
6
- ` react-testing-library ` provides the
7
- [ ` testHook ` ] ( /docs/react-testing-library/api#testhook ) utility to test custom
8
- hooks.
5
+ [ ` react-hooks-testing-library ` ] [ gh ] is built on top of [ ` react-testing-library ` ] [ gh ] to create a simple test harness for [ React hooks] ( https://reactjs.org/docs/hooks-intro.html ) that handles running them within the body of a function component, as well as providing various useful utility functions for updating the inputs and retrieving the outputs of your [ custom hook] ( https://reactjs.org/docs/hooks-custom.html ) .
9
6
10
7
> ** Note**
11
8
>
@@ -14,189 +11,60 @@ hooks.
14
11
> hooks. Typically those are better tested by testing the component that is
15
12
> using it.
16
13
17
- ## Using ` result `
18
-
19
- Testing the last returned value of a hook using the ` result ` ref
20
-
21
- ``` jsx
22
- function useCounter ({ initialCount = 0 , step = 1 } = {}) {
23
- const [count , setCount ] = React .useState (initialCount)
24
- const increment = () => setCount (c => c + step)
25
- const decrement = () => setCount (c => c - step)
26
- return { count, increment, decrement }
27
- }
28
14
```
29
-
30
- ``` jsx
31
- test (' returns result ref with latest result from hook execution' , () => {
32
- const { result } = testHook (useCounter)
33
- expect (result .current .count ).toBe (0 )
34
- act (() => {
35
- result .current .increment ()
36
- })
37
- expect (result .current .count ).toBe (1 )
38
- })
15
+ npm install --save-dev react-hooks-testing-library
39
16
```
40
17
41
- ## State
42
-
43
- Testing a hook that provides state
44
-
45
- ``` jsx
18
+ ``` js
19
+ // useCounter.js
46
20
import { useState } from ' react'
47
21
48
- export function useCounter ({ initialCount = 0 , step = 1 } = {} ) {
22
+ function useCounter (initialCount = 0 ) {
49
23
const [count , setCount ] = useState (initialCount)
50
- const increment = () => setCount (c => c + step)
51
- const decrement = () => setCount (c => c - step)
52
- return { count, increment, decrement }
24
+
25
+ const incrementBy = useCallback ((n ) => setCount (count + n), [count])
26
+ const decrementBy = useCallback ((n ) => setCount (count - n), [count])
27
+
28
+ return { count, incrementBy, decrementBy }
53
29
}
30
+
31
+ export default useCounter
54
32
```
55
33
56
- ``` jsx
57
- import { testHook , act , cleanup } from ' react-testing-library'
34
+ ``` js
35
+ // useCounter.test.js
36
+ import { renderHook , cleanup , act } from ' react-hooks-testing-library'
37
+ import useCounter from ' ./useCounter'
38
+
58
39
afterEach (cleanup)
59
40
60
- describe (' useCounter' , () => {
61
- test (' accepts default initial values' , () => {
62
- let count
63
- testHook (() => ({ count } = useCounter ()))
64
-
65
- expect (count).toBe (0 )
66
- })
67
-
68
- test (' accepts a default initial value for `count`' , () => {
69
- let count
70
- testHook (() => ({ count } = useCounter ({})))
71
-
72
- expect (count).toBe (0 )
73
- })
74
-
75
- test (' provides an `increment` function' , () => {
76
- let count, increment
77
- testHook (() => ({ count, increment } = useCounter ({ step: 2 })))
78
-
79
- expect (count).toBe (0 )
80
- act (() => {
81
- increment ()
82
- })
83
- expect (count).toBe (2 )
84
- })
85
-
86
- test (' provides an `decrement` function' , () => {
87
- let count, decrement
88
- testHook (() => ({ count, decrement } = useCounter ({ step: 2 })))
89
-
90
- expect (count).toBe (0 )
91
- act (() => {
92
- decrement ()
93
- })
94
- expect (count).toBe (- 2 )
95
- })
96
-
97
- test (' accepts a default initial value for `step`' , () => {
98
- let count, increment
99
- testHook (() => ({ count, increment } = useCounter ({})))
100
-
101
- expect (count).toBe (0 )
102
- act (() => {
103
- increment ()
104
- })
105
- expect (count).toBe (1 )
106
- })
41
+ test (' should create counter' , () => {
42
+ const { result } = renderHook (() => useCounter ())
43
+
44
+ expect (result .current .count ).toBe (0 )
107
45
})
108
- ```
109
46
110
- ## Unmount Side-Effects
47
+ test (' should increment counter' , () => {
48
+ const { result } = renderHook (() => useCounter ())
111
49
112
- Using the ` unmount ` function to check useEffect behavior when unmounting
50
+ act (() => result . current . incrementBy ( 1 ))
113
51
114
- ``` jsx
115
- import { useState , useEffect } from ' react'
52
+ expect (result .current .count ).toBe (1 )
116
53
117
- export function useDocumentTitle (title ) {
118
- const [originalTitle , setOriginalTitle ] = useState (document .title )
119
- useEffect (() => {
120
- setOriginalTitle (document .title )
121
- document .title = title
122
- return () => {
123
- document .title = originalTitle
124
- }
125
- }, [title])
126
- }
127
- ```
54
+ act (() => result .current .incrementBy (2 ))
128
55
129
- ``` jsx
130
- describe (' useDocumentTitle' , () => {
131
- test (' sets a title' , () => {
132
- document .title = ' original title'
133
- testHook (() => {
134
- useDocumentTitle (' modified title' )
135
- })
136
-
137
- expect (document .title ).toBe (' modified title' )
138
- })
139
-
140
- test (' returns to original title when component is unmounted' , () => {
141
- document .title = ' original title'
142
- const { unmount } = testHook (() => {
143
- useDocumentTitle (' modified title' )
144
- })
145
-
146
- unmount ()
147
- expect (document .title ).toBe (' original title' )
148
- })
56
+ expect (result .current .count ).toBe (3 )
149
57
})
150
- ```
151
58
152
- ## Rerender Side-Effects
59
+ test (' should decrement counter' , () => {
60
+ const { result } = renderHook (() => useCounter ())
153
61
154
- Using the ` rerender ` function to test calling useEffect multiple times
62
+ act (() => result . current . decrementBy ( 1 ))
155
63
156
- ``` jsx
157
- import { useEffect } from ' react'
64
+ expect (result .current .count ).toBe (- 1 )
158
65
159
- export function useCall (callback , deps ) {
160
- useEffect (() => {
161
- callback ()
162
- }, deps)
163
- }
164
- ```
66
+ act (() => result .current .decrementBy (2 ))
165
67
166
- ``` jsx
167
- describe (' useCall' , () => {
168
- test (' calls once on render' , () => {
169
- const spy = jest .fn ()
170
- testHook (() => {
171
- useCall (spy, [])
172
- })
173
- expect (spy).toHaveBeenCalledTimes (1 )
174
- })
175
-
176
- test (' calls again if deps change' , () => {
177
- let deps = [false ]
178
- const spy = jest .fn ()
179
- const { rerender } = testHook (() => {
180
- useCall (spy, deps)
181
- })
182
- expect (spy).toHaveBeenCalledTimes (1 )
183
-
184
- deps = [true ]
185
- rerender ()
186
- expect (spy).toHaveBeenCalledTimes (2 )
187
- })
188
-
189
- test (' does not call again if deps are the same' , () => {
190
- let deps = [false ]
191
- const spy = jest .fn ()
192
- const { rerender } = testHook (() => {
193
- useCall (spy, deps)
194
- })
195
- expect (spy).toHaveBeenCalledTimes (1 )
196
-
197
- deps = [false ]
198
- rerender ()
199
- expect (spy).toHaveBeenCalledTimes (1 )
200
- })
68
+ expect (result .current .count ).toBe (- 3 )
201
69
})
202
- ```
70
+ ```
0 commit comments