首页 > 解决方案 > 使用 useReducer/useContext 和 React-Testing-Library 时出现“TypeError: dispatch is not a function”

问题描述

我在测试通过 useReducer 和 React-testing-library 使用调度的组件时遇到问题。

我创建了一个不太复杂的示例来尝试总结正在发生的事情,但仍然存在同样的dispatch is not a function问题。当我运行测试时,我收到此错误:


      11 |         data-testid="jared-test-button"
      12 |         onClick={() => {
    > 13 |           dispatch({ type: 'SWITCH' })
         |           ^
      14 |         }}
      15 |       >
      16 |         Click Me

另外,如果我做一个console.log(typeof dispatch)inside RandomButton,然后单击输出显示的按钮function

这是有问题的测试。

import React from 'react'
import RandomButton from '../RandomButton'
import { render, fireEvent } from '@testing-library/react'

describe('Button Render', () => {
  it('click button', () => {
    const { getByTestId, queryByText } = render(<RandomButton />)

    expect(getByTestId('jared-test-button')).toBeInTheDocument()
    fireEvent.click(getByTestId('jared-test-button'))
    expect(queryByText('My name is frog')).toBeInTheDocument()
  })
})

这是我的相关代码:

随机按钮.js

import React, { useContext } from 'react'
import MyContext from 'contexts/MyContext'

const RandomButton = () => {
  const { dispatch } = useContext(MyContext)

  return (
    <div>
      <Button
        data-testid="jared-test-button"
        onClick={() => {
          dispatch({ type: 'SWITCH' })
        }}
      >
        Click Me
      </Button>
    </div>
  )
}

export default RandomButton

MyApp.js

import React, { useReducer } from 'react'
import {myreducer} from './MyFunctions'
import MyContext from 'contexts/MyContext'
import RandomButton from './RandomButton'

  const initialState = {
    blue: false,
  }
  const [{ blue },dispatch] = useReducer(myreducer, initialState)


return (
    <MyContext.Provider value={{ dispatch }}>
      <div>
            {blue && <div>My name is frog</div>}
            <RandomButton />
    </div>
    </MyContext.Provider>
)

export default MyApp

MyFunctions.js

export const myreducer = (state, action) => {
  switch (action.type) {
    case 'SWITCH':
      return { ...state, blue: !state.blue }
    default:
      return state
  }
}

MyContext.js

import React from 'react'

const MyContext = React.createContext({})

export default MyContext

我错过了一些愚蠢的东西,但是在阅读了文档并在线查看了其他示例之后,我没有看到解决方案。

标签: javascriptreactjsjestjsreact-testing-library

解决方案


我没有用 react-testing-library 测试过 redux 钩子,但我知道你必须为渲染函数提供一个包装器,为 Provider 提供调度函数。

这是我用来测试连接到 redux 存储的组件的示例:

testUtils.js

import React from 'react';
import { createStore } from 'redux';
import { render } from '@testing-library/react';
import { Provider } from 'react-redux';
import reducer from '../reducers';

// https://testing-library.com/docs/example-react-redux
export const renderWithRedux = (
  ui,
  { initialState, store = createStore(reducer, initialState) } = {},
  options,
) => ({
  ...render(<Provider store={store}>{ui}</Provider>, options),
  store,
});

因此,根据您分享的内容,我认为您想要的包装器看起来像这样:

import React from 'react';
import MyContext from 'contexts/MyContext';

// export so you can test that it was called with specific arguments
export dispatchMock = jest.fn();

export ProviderWrapper = ({ children }) => (
  // place your mock dispatch function in the provider
  <MyContext.Provider value={{ dispatch: dispatchMock }}>
    {children}
  </MyContext.Provider>
);

在你的测试中:

import React from 'react';
import RandomButton from '../RandomButton';
import { render, fireEvent } from '@testing-library/react';
import { ProviderWrapper, dispatchMock } from './testUtils';

describe('Button Render', () => {
  it('click button', () => {
    const { getByTestId, queryByText } = render(
      <RandomButton />,
      { wrapper: ProviderWrapper }, // Specify your wrapper here
    );

    expect(getByTestId('jared-test-button')).toBeInTheDocument();
    fireEvent.click(getByTestId('jared-test-button'));
    // expect(queryByText('My name is frog')).toBeInTheDocument(); // won't work since this text is part of the parent component

    // If you wanted to test that the dispatch was called correctly
    expect(dispatchMock).toHaveBeenCalledWith({ type: 'SWITCH' });
  })
})

就像我说的,我不必专门测试 redux 钩子,但我相信这应该能让你找到一个好地方。


推荐阅读