Slide 1

Slide 1 text

React Hooks ษڧձ vol.5 Hooksͷςετ 2020.6.18 @_yukikayuki

Slide 2

Slide 2 text

ࣗݾ঺հ • State • Kaneda Takayuki • Web Application Developer • झຯ • Splatoon2, ᲔΛ৯΂Δ @_yukikayuki

Slide 3

Slide 3 text

Scope • o Custom Hooksͷςετ • x React Componentͷςετ • x E2Eςετ

Slide 4

Slide 4 text

Agenda • Hooksͷ͓͞Β͍ • react-hooks-testing-libraryΛ࢖ͬͯςετ • useCounter

Slide 5

Slide 5 text

Hooksͷ͓͞Β͍

Slide 6

Slide 6 text

✌ ϑοΫͷϧʔϧ • ϑοΫ͸ؔ਺ͷτοϓϨϕϧͷΈͰݺͼग़ͯ͠ ͍ͩ͘͞ɻϧʔϓ΍৚݅෼ذ΍ωετͨؔ͠਺ ͷதͰϑοΫΛݺͼग़͞ͳ͍Ͱ͍ͩ͘͞ɻ • ϑοΫ͸ React ͷؔ਺ίϯϙʔωϯτͷ಺෦ͷ ΈͰݺͼग़͍ͯͩ͘͠͞ɻ௨ৗͷ JavaScript ؔ ਺಺Ͱ͸ݺͼग़͞ͳ͍Ͱ͍ͩ͘͞ɻ https://ja.reactjs.org/docs/hooks-overview.html#rules-of-hooks

Slide 7

Slide 7 text

Hooks͸ React Componentͷ֎Ͱ͸ ׆ಈͰ͖ͳ͍

Slide 8

Slide 8 text

ϧʔϧΛഁΔͱͲ͏ͳΔ͔

Slide 9

Slide 9 text

useCounter.js import {useState, useCallback} from 'react' export default function useCounter(initialValue = 0) { const [count, setCount] = useState(initialValue) const increment = useCallback(() => setCount((x) => x + 1), []) const reset = useCallback(() => setCount(initialValue), [initialValue]) return {count, increment, reset} }

Slide 10

Slide 10 text

useCount.test.js import useCounter from "./useCounter"; test('should use counter', () => { const {count} = useCounter() expect(count).toBe(0) })

Slide 11

Slide 11 text

jest useContainer.test.js Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.

Slide 12

Slide 12 text

react-hooks-testing-library

Slide 13

Slide 13 text

ͲͷϥΠϒϥϦΛ࢖͏΂͖͔ • ௐ΂ͨݶΓReact Componentͷςετ͸Enzyme ͱReact Testing Libraryͷ2ڧͬΆ͍ • testing-libraryϑΝϛϦʔʁͷreact-hooks-testing- library͕Hooks୯ମͰςετͰ͖Δ • react-useͰ΋࢖ͬͯΔΆ͍͠ɺͦΕ͕͍͍Μ͡Ό ͳ͍͔

Slide 14

Slide 14 text

؀ڥߏங • yarn create react-app my-app • cd my-app • yarn add -D @testing-library/react-hooks react-testing-renderer

Slide 15

Slide 15 text

useCounter.js import {useState, useCallback} from 'react' export default function useCounter(initialValue = 0) { const [count, setCount] = useState(initialValue) const increment = useCallback(() => setCount((x) => x + 1), []) const reset = useCallback(() => setCount(initialValue), [initialValue]) return {count, increment, reset} }

Slide 16

Slide 16 text

Rendering import {renderHook} from "@testing-library/react-hooks"; import useCounter from "./useCounter"; test('should use counter', () => { const {result} = renderHook(() => useCounter()) expect(result.current.count).toBe(0) expect(typeof result.current.increment).toBe('function') expect(typeof result.current.reset).toBe('function') })

Slide 17

Slide 17 text

Updates import {act, renderHook} from "@testing-library/react-hooks"; import useCounter from "./useCounter"; test('should increment counter', () => { const {result} = renderHook(() => useCounter()) act(() => { result.current.increment() }) expect(result.current.count).toBe(1) })

Slide 18

Slide 18 text

act() • ϨϯμʔɾϢʔβΠϕϯτɾσʔλͷऔಘͳͲ ͷλεΫʹؔ࿈͢Δߋ৽ॲཧ͕͢΂ͯॲཧ͞ ΕɺDOMʹ൓ө͞Ε͍ͯΔ͜ͱΛอূ͢Δ • ࣮ࡍͷϢʔβ͕ΞϓϦέʔγϣϯΛ࢖͏࣌ʹ ମݧ͢Δͷͱ͍ۙঢ়گͰ࣮ߦ͞ΕΔΑ͏ʹͳ Δ

Slide 19

Slide 19 text

jest noAct.test.js import {renderHook} from "@testing-library/react-hooks"; import useCounter from "./useCounter"; test('should increment counter', () => { const {result} = renderHook(() => useCounter()) result.current.increment() expect(result.current.count).toBe(1) })

Slide 20

Slide 20 text

act()ແ͠ͷWarning Warning: An update to TestHook inside a test was not wrapped in act(...). When testing, code that causes React state updates should be wrapped into act(...): act(() => { /* fire events that update state */ }); /* assert on the output */ This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act in TestHook in Suspense

Slide 21

Slide 21 text

Providing Props 1 import {act, renderHook} from "@testing-library/react-hooks"; import useCounter from "./useCounter"; test('should increment counter from custom initial value', () => { const {result} = renderHook(() => useCounter(9000)) act(() => { result.current.increment() }) expect(result.current.count).toBe(9001) })

Slide 22

Slide 22 text

Providing Props 2 import {act, renderHook} from "@testing-library/react-hooks"; import useCounter from "./useCounter"; test('should reset counter to updated initial value 1', () => { let initialValue = 0 const {result, rerender} = renderHook(() => useCounter(initialValue)) initialValue = 10 rerender() act(() => { result.current.reset() }) expect(result.current.count).toBe(10) })

Slide 23

Slide 23 text

Providing Props 3 import {act, renderHook} from "@testing-library/react-hooks"; import useCounter from "./useCounter"; test('should reset counter to updated initial value 2', () => { const {result, rerender} = renderHook(({initialValue}) => useCounter(initialValue), { initialProps: {initialValue: 0}, }) rerender({initialValue: 10}) act(() => { result.current.reset() }) expect(result.current.count).toBe(10) })

Slide 24

Slide 24 text

·ͱΊ

Slide 25

Slide 25 text

Hooks୯ମͷςετ͸ຊྲྀͰ͸ͳ͍ • Hooks୯ମͷςετʹ͍ͭͯ͸ެࣜυΩϡϝ ϯτʹॻ͍ͯͳ͍ • Componentͷςετʹ͍ͭͯ͸ެࣜυΩϡϝ ϯτ > Testingʹॻ͍ͯ͋Δ • Componentͱͯ͠Hooks΋ςετ͍ͯ͠Δ

Slide 26

Slide 26 text

react-hooks-testing-libraryʹ ॏཁͳ͜ͱ͕ॻ͔Ε͍ͯΔ NOTE it is not recommended to test single-use custom hooks in isolation from the components where it's being used. It's better to test the component that's using the hook rather than the hook itself. The React Hooks Testing Library is intended to be used for reusable hooks/libraries. ஫ɿ࢖͍ࣺͯͷΧελϜϑοΫΛɺͦΕ͕࢖༻͞Ε͍ͯΔίϯϙʔ ωϯτ͔Β੾Γ཭ͯ͠ςετ͢Δ͜ͱ͸͓קΊ͠·ͤΜɻϑοΫࣗ ମͰ͸ͳ͘ɺϑοΫΛ࢖༻͍ͯ͠ΔίϯϙʔωϯτΛςετ͢Δ͜ ͱΛ͓קΊ͠·͢ɻReact Hooks Testing Library ࠶ར༻Մೳͳϑο Ϋ/ϥΠϒϥϦͷͨΊʹ࢖༻͞ΕΔ͜ͱΛҙਤ͍ͯ͠·͢ɻ https://github.com/testing-library/react-testing-library#hooks

Slide 27

Slide 27 text

࣍ͷֶशύε • react-hooks-testing-libraryͷAdvanced HooksΛ΍ͬͯΈΔ • YoutubeͰ’ How To Test Custom Effect Hook - Custom Hooks Mini Course Part 5’ΛݟΔ • react-useͷςετίʔυΛ؍࡯͢Δ

Slide 28

Slide 28 text

ྑ͍ςετϥΠϑΛ