我有一个使用
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
});
});
但是,这似乎没有按预期工作,因为内部模拟似乎没有覆盖顶级模拟。我可以知道如何做到这一点吗?我搜索过其他类似的线程,但它们效果不佳。
我设法通过模拟
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',
},
});
...
});
我很想知道是否有人有更好的方法。
我的做法是定义一个
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' ()=>{})
在下一个开始之前完成。