【问题标题】:Mocking react-router-dom hooks using jest is not working使用 jest 模拟 react-router-dom 钩子不起作用
【发布时间】:2020-03-11 23:49:03
【问题描述】:

我正在使用 Enzyme 的浅层方法来测试一个组件,该组件使用 useParams 钩子从 URL 参数中获取 ID。

我正在尝试模拟 useParams 钩子,以便它不会调用实际方法,但它不起作用。我仍然得到TypeError: Cannot read property 'match' of undefined,所以它调用实际的useParams,而不是我的模拟。

我的组件:

import React from 'react';
import { useParams } from 'react-router-dom';

export default () => {

  const { id } = useParams();

  return <div>{id}</div>;

};

测试:

import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import React from 'react';
import Header from './header';
import { shallow } from 'enzyme';

Enzyme.configure({ adapter: new Adapter() });

describe('<Header />', () => {

  jest.mock('react-router-dom', () => ({
    useParams: jest.fn().mockReturnValue({ id: '123' }),
  }));

  it('renders', () => {
    const wrapper = shallow(<Header />);
    expect(wrapper).toBeTruthy();
  });

});

谢谢!

【问题讨论】:

    标签: reactjs react-router jestjs enzyme react-hooks


    【解决方案1】:

    这对我来说可以模拟 useParams 并更改同一文件中每个单元测试的值:

    import React from "react";
    import { render } from "@testing-library/react";
    import Router from "react-router-dom";
    import Component from "./Component";
    
    jest.mock("react-router-dom", () => ({
     ...jest.requireActual("react-router-dom"),
     useParams: jest.fn(),
    }));
    
    const createWrapper = () => {
     return render(<Cases />);
    };
    
    describe("Component Page", () => {
     describe("Rendering", () => {
       it("should render cases container", () => {
         jest.spyOn(Router, 'useParams').mockReturnValue({ id: '1234' })
         const wrapper = createWrapper();
         expect(wrapper).toMatchSnapshot();
       });
    
       it("should render details container", () => {
         jest.spyOn(Router, 'useParams').mockReturnValue({ id: '5678' })
         const wrapper = createWrapper();
         expect(wrapper).toMatchSnapshot();
       });
     });
    });
    
    

    只需在 describe() 外部将 useParams 声明为 jest.fn(),然后在每个单元测试中使用 jest.spyOn 更改其值

    【讨论】:

      【解决方案2】:

      我不知道为什么,在 react-router 库的文档中也找不到它,但是在测试和实现中将 react-router-dom 更改为 react-router 对我有用。

      所以它变成了这样:

      import Enzyme from 'enzyme';
      import Adapter from 'enzyme-adapter-react-16';
      import React from 'react';
      import Header from './header';
      import { shallow } from 'enzyme';
      
      Enzyme.configure({ adapter: new Adapter() });
      
      describe('<Header />', () => {
      
        jest.mock('react-router', () => ({
          useParams: jest.fn().mockReturnValue({ id: '123' }),
        }));
      
        it('renders', () => {
          const wrapper = shallow(<Header />);
          expect(wrapper).toBeTruthy();
        });
      
      });
      

      【讨论】:

      • 感谢@ivan,如果有人知道其背后的逻辑,请在此处发布。
      • 好吧,我想我找到了:react-router-dom 没有定义钩子。钩子在react-router 包中定义。因此,如果你想模拟反应路由器钩子,你需要模拟那个包。见:github.com/ReactTraining/react-router/blob/master/packages/…
      • 即使我从 react-router 导入这些钩子并模拟 react-router 包,它仍然给他们同样的错误 Cannot read property 'match' of undefined
      【解决方案3】:

      我尝试了这个模拟,但它对我不起作用。错误:无法读取未定义的属性“匹配”。似乎该组件不在路由器内部,因此它无法使用参数模拟匹配。它对我有用:

      import { MemoryRouter, Route } from 'react-router-dom';
      
      const RenderWithRouter = ({ children }) => (
        <MemoryRouter initialEntries={['uri/Ineed']}>
          <Route path="route/Ineed/:paramId">{children}</Route>
        </MemoryRouter>
      );
      const tf = new TestFramework();
      describe('<MyComponent />', () => {
        tf.init({ title: 'Some test' }, props =>
          shallow(
            <RenderWithRouter>
              <MyComponent {...props} />
            </RenderWithRouter>
          )
        );
      
        it('Some description', () => {
          const wrapper = tf.render().html();
          expect(wrapper).toContain('something');
        });
      });
      

      【讨论】:

        【解决方案4】:

        我遇到过类似的问题,我是这样解决的:

        import { Route, Router } from "react-router-dom";
        import { createMemoryHistory } from "history";
        
        const renderWithRouter = (component) => {
          const history = createMemoryHistory({
            initialEntries: ["/part1/idValue1/part2/idValue2/part3"],
          });
          const Wrapper = ({ children }) => (
            <Router history={history}>
              <Route path="/part1/:id1/part2/:id2/part3">{children}</Route>
            </Router>
          );
          return {
            ...render(component, { wrapper: Wrapper }),
            history,
          };
        };
        
        describe("test", () => {
          it("test desc", async () => {
            const { getByText } = renderWithRouter(<MyComponent/>);
            expect(getByText("idValue1")).toBeTruthy();
          });
        });
        

        【讨论】:

        • 我忘了说,我正在使用 react-testing-library 但我希望路由器部分至少是相似的
        • 这正是我所需要的。谢谢!
        【解决方案5】:

        我遇到了同样的问题。我像这样嘲笑 useParams:

        jest.mock('react-router-dom', () => {
          return {
            useParams: () => ({
              id: '123'
            })
          }
        })
        

        【讨论】:

          【解决方案6】:

          对我来说,嘲笑 react-router-dom 解决问题:

          jest.mock('react-router-dom', () => ({
              useParams: jest.fn().mockReturnValue({ nifUuid: 'nif123' }),
              useHistory: jest.fn()
          }));
          

          【讨论】:

            【解决方案7】:

            我遇到了同样的问题。 从“@testing-library/react”调用“清理”函数对我有帮助:

            import { cleanup } from '@testing-library/react';
            
            afterEach(() => {
                cleanup();
            });
            

            【讨论】:

            • 一般来说,如果您的解决方案与您的解决方案不完全相同,那么提供帮助您找到此解决方案的步骤可能对其他人非常有用,除此之外,很好的干净答案。
            猜你喜欢
            • 2018-12-04
            • 1970-01-01
            • 1970-01-01
            • 2020-05-19
            • 1970-01-01
            • 2018-07-31
            • 2021-01-10
            • 2020-12-22
            • 2021-05-11
            相关资源
            最近更新 更多