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)
})
})