使用 jest 模拟 react-router-dom 钩子不起作用

Posted

技术标签:

【中文标题】使用 jest 模拟 react-router-dom 钩子不起作用【英文标题】:Mocking react-router-dom hooks using jest is not working 【发布时间】: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();
  );

);

谢谢!

【问题讨论】:

【参考方案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();
);

【讨论】:

一般来说,如果您的解决方案与您的解决方案不完全相同,那么提供帮助您找到此解决方案的步骤可能对其他人非常有用,除此之外,很好的干净答案。

以上是关于使用 jest 模拟 react-router-dom 钩子不起作用的主要内容,如果未能解决你的问题,请参考以下文章

Jest 手动模拟在 CRA 中不使用模拟文件

使用 jest.mock('axios') 时如何模拟拦截器?

如何使用 Jest 模拟异步函数

在 Typescript 中使用 Jest + Supertest 进行模拟

手动模拟不适用于 Jest

使用 Typescript 从 Jest 手动模拟中导入函数