我正在尝试测试一个使用react-redux进行状态管理的组件。
为了快速测试,我想模拟 useSelector,如下所示:
const templates = [
{
id: ...,
name: ...,
...
},
{
id: ...,
name: ...,
...
},
]
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useSelector: jest.fn(),
}));
describe('some awesome description', () => {
beforeEach(() => {
useSelector.mockImplementation(callback => callback(templates));
});
});
但是在运行测试时,会失败,如下所示:
TypeError: Cannot read properties of undefined (reading 'ids')
141 | describe('layouts/TemplatesPanel', () => {
142 | beforeEach(() => {
> 143 | useSelector.mockImplementation(callback => callback(templates));
| ^
你最好不要mock
useSelector
,mock 的实现可能会破坏它的功能,因为它的功能不仅仅是返回某个状态片。请参阅 useSelector
真正的实现,它不仅仅返回 selectedState
。
推荐的方法是创建一个模拟商店并为其提供模拟数据。
例如
index.tsx
:
import React from 'react';
import { useSelector } from 'react-redux';
export type Template = {
id: string;
name: string;
};
export type RootState = {
templates: Template[];
};
export const MyComp = () => {
const templates = useSelector<RootState>((state) => state.templates);
console.log('templates: ', templates);
return <div>MyComp</div>;
};
index.test.tsx
:
import { render } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { MyComp, RootState, Template } from '.';
describe('73494842', () => {
test('should pass', () => {
const templates: Template[] = [
{ id: '1', name: 'a' },
{ id: '2', name: 'b' },
];
const mockStore = createStore<RootState, any, any, any>((state = { templates }, action) => {
if (action.type === 'UPATE_NAME') {
return {
...state,
templates: templates.map((t) => (t.id === action.payload.id ? { ...t, name: action.payload.name } : t)),
};
}
return state;
});
render(
<Provider store={mockStore}>
<MyComp />
</Provider>
);
mockStore.dispatch({ type: 'UPATE_NAME', payload: { id: '1', name: 'c' } });
});
});
测试结果:
73494842
✓ should pass (44 ms)
console.log
templates: [ { id: '1', name: 'a' }, { id: '2', name: 'b' } ]
at MyComp (stackoverflow/73494842/index.tsx:14:11)
console.log
templates: [ { id: '1', name: 'c' }, { id: '2', name: 'b' } ]
at MyComp (stackoverflow/73494842/index.tsx:14:11)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 10.931 s, estimated 11 s
当我们稍后调度一个动作时,
useSelector
钩子将订阅商店的更改,它将再次执行并获取更新的状态切片。如果你模拟它只是返回一个状态切片,那么这个功能就不再起作用了。
您还需要在
jest.mock
中传递回调函数。
useSelector: (callback) => jest.fn(callback)