Masahiro Okubo

ReactのuseContextのテストでお世話になった参考コード

ReactのuseContextのテストでお世話になった参考コード

ReactでuseContextのテストの実装を試みたところ、最初だったので結構詰まってしました。。。

色々と参考の記事を探したのですが、こちらのコードとzennのテキストが非常に参考となったため、
ほぼそのまま利用してそれをテスト記述しました🙇‍♂️

※こちらのコードはzennから参照させていただいております。リンクはブログの最下部に記載。

// contextのコード
import { createContext, useCallback, useContext, useReducer } from "react"

type AlertState = {
  show: boolean
  message: string
}
type AlertDispatch = {
  showDispatcher: (message: string) => void
  hideDispatcher: () => void
}
const initialState = {
  show: false,
  message: ""
}
const alertStateContext = createContext<AlertState>(initialState)
const alertDispatchContext = createContext<AlertDispatch>({
  showDispatcher: () => void 0,
  hideDispatcher: () => void 0
})

export const useAlertState = () => useContext(alertStateContext)
export const useAlertDispatch = () => useContext(alertDispatchContext)

export function AlertProvider({ children }: { children: JSX.Element[] | JSX.Element }) {
  const [state, dispatch] = useReducer(reducer, initialState)
  const showDispatcher = useCallback((message: string) => dispatch(showAlert(message)), [])
  const hideDispatcher = useCallback(() => dispatch(hideAlert()), [])
  return (
    <alertStateContext.Provider value={state}>
      <alertDispatchContext.Provider value={{ showDispatcher, hideDispatcher }}>
        {children}
      </alertDispatchContext.Provider>
    </alertStateContext.Provider>
  )
}

type AlertAction = ReturnType<typeof showAlert> | ReturnType<typeof hideAlert>

function reducer(state: AlertState, action: AlertAction) {
  switch (action.type) {
    case "ui/alert:show":
      return {
        ...state,
        show: true,
        message: action.payload.message
      }
    case "ui/alert:hide":
      return {
        ...state,
        show: false
      }
    default:
      return state
  }
}

function showAlert(message: string) {
  return {
    type: "ui/alert:show",
    payload: {
      message
    }
  } as const
}

function hideAlert() {
  return {
    type: "ui/alert:hide"
  } as const
}

// テストコード
import { act, cleanup, renderHook, RenderResult } from "@testing-library/react-hooks"

import { AlertProvider, useAlertDispatch, useAlertState } from "./AlertContext"

function wrapper({ children }: { children: JSX.Element[] }) {
  return <AlertProvider>{children}</AlertProvider>
}

function useAlertContextTest() {
  const { show, message } = useAlertState()
  const { showDispatcher, hideDispatcher } = useAlertDispatch()
  return {
    show,
    message,
    showDispatcher,
    hideDispatcher
  }
}

describe("AlertContext", () => {
  let renderResult: RenderResult<ReturnType<typeof useAlertContextTest>>

  beforeEach(() => {
    const { result } = renderHook(() => useAlertContextTest(), { wrapper })
    renderResult = result
  })
  afterEach(() => {
    cleanup()
  })

  test("context: initial state", () => {
    expect(renderResult.current.show).not.toBe(true)
    expect(renderResult.current.message).toBe("")
  })
  test("context: update state", () => {
    // show
    act(() => {
      renderResult.current.showDispatcher("test message")
    })
    expect(renderResult.current.show).toBe(true)
    expect(renderResult.current.message).toBe("test message")
    // hide
    act(() => {
      renderResult.current.hideDispatcher()
    })
    expect(renderResult.current.show).not.toBe(true)
  })
})

参考記事

Context を扱うコンポーネント:ユニットテスト

https://github.com/tkdn/react-testing-sandbox/blob/d564b0e24a3829d0d52fc170a2a559c71fad67ac/src/context/AlertContext.test.tsx

https://github.com/tkdn/react-testing-sandbox/blob/d564b0e24a3829d0d52fc170a2a559c71fad67ac/src/context/AlertContext.tsx

Buy Me A Coffee

関連記事

copyright© 2016-2021 Masahiro Okubo