Upgrade to Pro — share decks privately, control downloads, hide ads and more …

React Hooksのテストを書いてみよう

markey
August 08, 2019

React Hooksのテストを書いてみよう

React Hooksのテストを書いてみよう

markey

August 08, 2019
Tweet

Other Decks in Programming

Transcript

  1. こんないいことが UI とロジックが分離され、テスタブルなコードに ビジネスロジックをCustom Hook に集約する(API からのデータ取 得・加工、状態、ハンドラー) UI コンポーネントの開発は受け取るProps

    の定義と、それらを元に した表示のみ UI コンポーネント開発者はロジック部分やAPI の仕様などを一切知 る必要がない 複数の開発者が同一ページのUI とロジックを並行開発することが可 能
  2. パッケージの追加 yarn add --dev @testing-library/react-hooks yarn add --dev @testing-library/react-hooks #

    もしくは # もしくは npm install --save-dev @testing-library/react-hooks npm install --save-dev @testing-library/react-hooks
  3. @testing-library/react-hooks を使ったシンプルなテスト import import { { useState useState, , useCallback

    useCallback } } from from 'React' 'React' export export default default function function useCounter useCounter( () ) { { const const [ [count count, , setCount setCount] ] = = useState useState( (0 0) ) const const increment increment = = useCallback useCallback( (( () ) => => setCount setCount( (( (x x) ) => => x x return return { { count count, , increment increment } } } } import import { { renderHook renderHook } } from from '@testing-library/react-hook '@testing-library/react-hook import import useCounter useCounter from from '.' '.' test test( ('should use counter' 'should use counter', , ( () ) => => { {
  4. ブラウザ上でのhook の動作をシミュレートして、 値を更新するには? test test( ('should increment counter' 'should increment

    counter', , ( () ) => => { { const const { { result result } } = = renderHook renderHook( (( () ) => => useCounter useCounter( () )) ) expect expect( (result result. .current current. .count count) ). .toBe toBe( (0 0) ) // act でラップする // act でラップする act act( (( () ) => => { { result result. .current current. .increment increment( () ) } }) ) expect expect( (result result. .current current. .count count) ). .toBe toBe( (1 1) ) } }) )
  5. Context から値を取得するときはどうする? (Redux, Apollo Client, ...) const const CounterStepContext CounterStepContext

    = = React React. .createContext createContext( (1 1) ) export export const const CounterStepProvider CounterStepProvider = = ( ({ { step step, , children children } }) ) < <CounterStepContext CounterStepContext. .Provider value Provider value= ={ {step step} }> >{ {children children} }< < ) ) export export function function useCounter useCounter( () ) { { const const [ [count count, , setCount setCount] ] = = useState useState( (0 0) ) // increment する値はContext から取得 // increment する値はContext から取得 const const step step = = useContext useContext( (CounterStepContext CounterStepContext) ) const const increment increment = = useCallback useCallback( (( () ) => => setCount setCount( (( (x x) ) => => x x
  6. import import { { useCounter useCounter, , CounterStepProvider CounterStepProvider }

    } from from '.' '.' test test( ('should use custom step when incrementing' 'should use custom step when incrementing', , ( () ) => => const const wrapper wrapper = = ( ({ { children children } }) ) => => ( ( < <CounterStepProvider step CounterStepProvider step= ={ {2 2} }> >{ {children children} }< </ /CounterSt CounterSt ) ) // renderHook の第2 引数にwrapper オプションを指定 // renderHook の第2 引数にwrapper オプションを指定 const const { { result result } } = = renderHook renderHook( (( () ) => => useCounter useCounter( () ), , { { w w expect expect( (result result. .current current. .count count) ). .toBe toBe( (0 0) ) act act( (( () ) => => { { result result. .current current. .increment increment( () ) } }) )
  7. 非同期な関数の実行はどうする? export export default default function function useCounter useCounter( ()

    ) { { const const [ [count count, , setCount setCount] ] = = useState useState( (0 0) ) const const increment increment = = useCallback useCallback( (( () ) => => setCount setCount( (( (x x) ) => => x x // 0.1 秒後に非同期にincrement する // 0.1 秒後に非同期にincrement する const const incrementAsync incrementAsync = = useCallback useCallback( (( () ) => => setTimeout setTimeout( (i i return return { { count count, , increment increment, , incrementAsync incrementAsync } } } }
  8. test test( ('should increment counter after delay' 'should increment counter

    after delay', , async async ( () ) = = const const { { result result, , waitForNextUpdate waitForNextUpdate } } = = renderHook renderHook( (( () ) = = result result. .current current. .incrementAsync incrementAsync( () ) // 実行直後はまだincrement されていない // 実行直後はまだincrement されていない expect expect( (result result. .current current. .count count) ). .toBe toBe( (0 0) ) // Promise がresolve されるまで待つ // Promise がresolve されるまで待つ await await waitForNextUpdate waitForNextUpdate( () ) expect expect( (result result. .current current. .count count) ). .toBe toBe( (1 1) ) } }) )