覆盖react-router-dom和useHistory钩子的jest.mock

问题描述 投票:0回答:2

我有一个使用

useHistory
钩子的组件。

当我编写单元测试时,我模拟了

react-route-dom
模块,以便在正在测试的组件中正确模拟
useHistory
钩子。

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useHistory: () => ({
    location: {
      pathname: 'home',
    },
  }),
}));

但是,在其中一个测试(名为

testB
)中,我想覆盖
location.pathname
,使得
pathname
具有与默认模拟值不同的值 (
help
)..

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useHistory: () => ({
    location: {
      pathname: '/home',
    },
  }),
}));

describe('testing ComponentA', () => {
   afterEach(() => {
      jest.clearAllMocks();
   });

  it('test A', () => {
    // some test
  });

  it('test B', () => {
    jest.mock('react-router-dom', () => ({
      ...jest.requireActual('react-router-dom'),
      useHistory: () => ({
        location: {
          pathname: '/help',
       },
     }),
    }));
    // expect ...
  });

  it('test C', () => {
    // some test
  });
});

但是,这似乎没有按预期工作,因为内部模拟似乎没有覆盖顶级模拟。我可以知道如何做到这一点吗?我搜索过其他类似的线程,但它们效果不佳。

reactjs react-router jestjs react-hooks
2个回答
2
投票

我设法通过模拟

react-router-dom
文件夹中的
__mocks__
库来归档类似的内容,然后在每个测试中单独模拟钩子,我需要用
mockReturnValue
覆盖它的返回值。

// /__mocks__/react-router-dom.js
module.exports = {
  ...jest.requireActual('react-router-dom'),
  useLocation: jest.fn(), // --> you may use useHistory instead
};
it('Should ...', () => {
  // Here mock what you want to test
  useLocation.mockReturnValue({
      search:"?foo=bar",
  });
       ...
  });

在您的情况下,使用

useHistory
应该类似于

it('Should ...', () => {
  // Here mock what you want to test
  useHistory.mockReturnValue({
   location: {
      pathname: '/home',
    },
  });
       ...
  });

我很想知道是否有人有更好的方法。


0
投票

我的做法是定义一个

mockPathname
变量,使模拟函数的返回值基于此变量,并将 mockPathname 设置为每个测试所期望的值:

let mockPathname = '/home'; // This variable is in scope to all the code below.

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useHistory: () => ({
    location: {
      pathname: mockPathname,
    },
  }),
}));

describe('testing ComponentA', () => {
   afterEach(() => {
      jest.clearAllMocks();
   });

  it('test A', () => {
    // some test that expects pathname to '/home' - the initial value above
  });

  it('test B', () => {
    mockPathname = '/other-for-test-b';
    // now run the tests that expect the pathname to be 'other-for-test-b'
    // expect ...
  });

  it('test C', () => {
    mockPathname = '/test-c-home';
    // some test expecting /test-c-home
  });
});

了解不同变量的范围以及执行顺序在这里至关重要。因为mockPathname处于范围的最高级别,所以我们可以根据需要进行更改,并且每次调用mock函数时,它将使用它的当前值,因为mock函数和测试都使用相同的实例同一范围内的变量。

如果您同时运行测试(如果它们是异步的,则可能会出现这种情况),您可能会遇到麻烦,因为一个测试可能会在前一个测试完成之前“覆盖”该值。仍然只有一个 mockPathname 的“副本”,如果两个测试同时运行,就会出现竞争条件,因为它们都将查看同一个变量。如果您按照下面的示例构建代码,这不会成为问题 - 每个

its('test' ()=>{})
在下一个开始之前完成。

© www.soinside.com 2019 - 2024. All rights reserved.