如何测试自定义用途使用酶获取钩子

时间:2019-09-17 19:00:11

标签: reactjs unit-testing jestjs enzyme react-hooks

有!

我在这里有一个useFetch,它将使用useReducer两次触发组件的状态更改

type AsyncType = 'INIT' | 'PENDING' | 'RESOLVED' | 'REJECTED' | 'REDIRECTED'

type AsyncAction =
  | { type: 'PENDING' }
  | { type: 'RESOLVED'; data: any }
  | { type: 'REJECTED'; error: Error }
  | { type: 'REDIRECTED' }

interface AsyncState {
  data?: any
  error?: Error
  type: AsyncType
}

const dataFetchReducer = (
  state: AsyncState,
  action: AsyncAction
): AsyncState => {
  switch (action.type) {
    case 'PENDING':
      return {
        ...state,
        type: 'PENDING',
      }
    case 'RESOLVED':
      return {
        ...state,
        type: 'RESOLVED',
        data: action.data,
      }
    // We can choose to ignore it, retry it or throw it to let the error boundary to catch it.
    case 'REJECTED':
      return {
        ...state,
        type: 'REJECTED',
        error: action.error,
      }
    case 'REDIRECTED':
      return {
        ...state,
        type: 'REDIRECTED',
      }
    default:
      throw new Error()
  }
}

// We can ignore the input if we don't want it to fetch new data when the component just mounted
export const useFetch = (
  initialRequestConfig?: AxiosRequestConfig,
  initialData?: any
): [AsyncState, Dispatch<AxiosRequestConfig>] => {
  const [requestConfig, setRequestConfig] = useState(initialRequestConfig)

  const [state, dispatch] = useReducer<typeof dataFetchReducer>(
    dataFetchReducer,
    {
      type: 'INIT',
      data: initialData,
    }
  )

  useEffect(() => {
    if (!requestConfig) return
    let didCancel = false
    const fetchData = async () => {
      dispatch({ type: 'PENDING' })
      try {
        const result = await axios(requestConfig)
        if (!didCancel) {
          dispatch({ type: 'RESOLVED', data: result.data })
        }
      } catch (error) {
        if (!didCancel) {
          if (
            error.response &&
            error.response.data &&
            error.response.data.redirect
          ) {
            dispatch({ type: 'REDIRECTED' })
          } else {
            dispatch({ type: 'REJECTED', error })
          }
        }
      }
    }
    fetchData()
    return () => {
      didCancel = true
    }
  }, [requestConfig])
  return [state, setRequestConfig]
}

但是,我发现很难为使用它的任何组件编写单元测试。例如,我们有一个像这样的组件

export const PrivateRoute: FC<RouteProps> = ({
  component: Component,
  ...rest
}) => {
  const [state] = useFetch(api.getUser()) // the api with only return the axios config, like this: { method: "GET", url: '/user' }
  if (!Component) return null
  console.log(state)
  return (
    <Route
      {...rest}
      render={props =>
        state.type === 'PENDING' ? (
          <p>Loading</p>
        ) : state.type === 'RESOLVED' ? (
          <Component {...props} />
        ) : state.type === 'REJECTED' ? (
          <Error err={state.error} />
        ) : null
      }
    />
  )
}

当我尝试测试时。不管我做什么我不能让它呈现<Component />而不是<div>Loading</div>

我只是这样模仿轴颈

import axios, { AxiosStatic } from 'axios'

interface AxiosMock extends AxiosStatic {
  mockResolvedValue: Function
  mockRejectedValue: Function
}

jest.mock('axios')
const mockedAxios = axios as AxiosMock

然后我尝试像这样测试我的组件

it('renders without crashing', async () => {
    const MockComp = () => <p>Test</p>
    mockedAxios.mockResolvedValue({ data: { user: 'caso' } })
    let wrapper
    await act(() => {
      wrapper = mount(
        <MemoryRouter initialEntries={['/random']}>
          <PrivateRoute path="/" component={MockComp} />
        </MemoryRouter>
      )
      wrapper.update() // wherever I put the update, the wrapper is always loading
    })
    console.log(wrapper.debug()) // this line will always be loading
    expect(wrapper.find(Route).prop('path')).toBe('/')
  })

总会有这样的警告 Warning: An update to PrivateRoute inside a test was not wrapped in act(...).

我不知道测试它的正确方法是什么。我花了两天时间。有人知道什么是正确的测试方法吗?我已经升级为反应16.9

1 个答案:

答案 0 :(得分:0)

我刚刚找到了最佳的解决方案,我们可以使用jest模拟useFetch,我们可以简单地返回模拟状态并测试渲染结果。