@testing-library/react-hookとJestでReact Hookをテストする

「React #2 Advent Calendar 2020」4日目の記事です。 @testing-library/react-hookを使うと、React Hookのユニットテストがかけます。 Install テスト対 […]

広告ここから
広告ここまで

目次

    React #2 Advent Calendar 2020」4日目の記事です。

    @testing-library/react-hookを使うと、React Hookのユニットテストがかけます。

    Install

    $ yarn add -D  react-@testing-library/react-hooks react-test-renderer

    テスト対象のフックを作ってみる

    動作確認のために、適当なフックを作ってみましょう。

    
    const useExampleModalHook = () => {
        const [modal, modalOpen] = useState(false)
        const open = useCallback(() => {
          modalOpen(true)
        }, [modalOpen])
        const close = useCallback(() => {
          modalOpen(false)
        }, [modalOpen])
        const toggle = useCallback(() => {
          modalOpen(!modal)
        }, [modal, modalOpen])
        return {
          modal,
          open,
          close,
          toggle,
        }
      }

    テストコードはこんな感じです。

    import { renderHook, act } from '@testing-library/react-hooks';
    
    describe('useExampleModalHook', () => {
      let result = renderHook(() => useExampleModalHook()).result
      beforeEach(() => {
        result = renderHook(() => useExampleModalHook()).result
      })
      afterEach(() => {
        mockDispatch.mockClear();
      });
      it('should modal is false by default', () => {
        expect(result.current.modal).toEqual(false)
      })
      it('should modal is true when call open', () => {
        act(() => {
          result.current.open()
        })
        expect(result.current.modal).toEqual(true)
      })
      it('should modal is true when called toggle once', () => {
        act(() => {
          result.current.toggle()
        })
        expect(result.current.modal).toEqual(true)
      })
      it('should modal is true when called toggle once', () => {
        act(() => {
          result.current.toggle()
        })
        act(() => {
          result.current.toggle()
        })
        expect(result.current.modal).toEqual(false)
      })
    })

    renderHook を使うことで、hook単体でテストコードが書けるようになります。

    また actを使って関数を実行したり、unmountなどもテストしていけます。

    With Redux (useDispatch / useSelector)

    ただしReduxが絡むとモックが必要になります。

    export const exampleHookWithRedux = () => {
      const dispatch = useDispatch();
      const globalClose = useCallback(() => {
        dispatch(closeUserModal('username'));
      }, [dispatch]);
      return {
        dummy: useSelector((state: State) => state.modal),
        globalClose,
      }
    }

    以下のようにJestでモックしてやりましょう。

    const mockDispatch = jest.fn();
    const mockSelector = jest.fn().mockImplementation(() => ({
      status: '',
      result: '',
    }));
    jest.mock('react-redux', () => ({
      useDispatch: () => mockDispatch,
      useSelector: () => mockSelector(),
    }));
    
    
    describe('exampleHookWithRedux ', () => {
      let result = renderHook(() => exampleHookWithRedux ()).result
      beforeEach(() => {
        result = renderHook(() => exampleHookWithRedux ()).result
      })
      it('should modal is false by default', () => {
        expect(result.current.dummy).toEqual({
          status: '',
          result: '',
        })
      })
      it('should call dispatch function at once', () => {
        act(() => {
          result.current.globalClose()
        })
        expect(mockDispatch).toHaveBeenCalledTimes(1)
      })
      it('should call valid action', () => {
        act(() => {
          result.current.globalClose()
        })
        expect(mockDispatch).toHaveBeenCalledWith({
          type: 'CLOSE_USER_MODAL',
          payload: {
            username: 'username'
          }
        })
      })
      it('When selector return updated value, should get updated one', () => {
        mockSelector.mockImplementationOnce(() => ({
          status: 'loading',
          result: ''
        }))
        result = renderHook(() => exampleHookWithRedux ()).result
        expect(result.current.dummy).toEqual({
          status: 'loading',
          result: '',
        })
      })
    })

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark