使用 SetTimeout 测试自定义钩子,使用 Jest 测试 useEffect
Posted
技术标签:
【中文标题】使用 SetTimeout 测试自定义钩子,使用 Jest 测试 useEffect【英文标题】:Testing custom hook with SetTimeout and useEffect with Jest 【发布时间】:2021-12-26 05:56:59 【问题描述】:我正在尝试测试一个使用 useEffect 和 setTimeout 的相对简单的自定义挂钩。但是,我的测试失败了,我不知道出了什么问题。
这里是钩子本身(useTokenExpirationCheck.ts)
import useEffect from 'react';
import logout from '../features/profile/profileSlice';
import useAppDispatch from '../store/hooks';
export default function useTokenExpirationCheck(exp: number): void
const dispatch = useAppDispatch();
useEffect(() =>
if (exp)
const timeToLogout = exp * 1000 - Date.now();
setTimeout(() =>
dispatch(logout());
, timeToLogout);
// eslint-disable-next-line react-hooks/exhaustive-deps
, [exp]);
和我的测试文件:
import act, renderHook from '@testing-library/react-hooks';
import useTokenExpirationCheck from './useTokenExpirationCheck';
jest.mock('../features/profile/profileSlice');
const logout = jest.fn();
const exp = Date.now() + 6000;
describe('Expiration token', () =>
test('should logout user', async () =>
jest.useFakeTimers();
act(() =>
renderHook(() =>
useTokenExpirationCheck(exp);
);
);
expect(logout).toHaveBeenCalledTimes(0);
jest.advanceTimersByTime(60000);
expect(logout).toHaveBeenCalledTimes(1);
);
);
我所知道的是 exp 变量没有传递给 useTokenExpirationCheck 函数(console.log 在函数内部显示 0,当它被执行时)。所以基本上,我什至没有接触到 useEffect 本身......有什么想法会出错吗?
【问题讨论】:
【参考方案1】:这是我的测试策略:
-
我将使用redux-mock-store 创建一个模拟商店
模拟存储将创建一个已调度的操作数组,用作测试的操作日志。
这样我就可以通过store.getActions()
方法获取并断言调度的操作。
我将使用模拟返回值模拟Date.now()
方法,以便测试不再依赖系统日期。不同时区和不同 CI/CD 服务器的系统日期可能不同。
使用Props更新输入(useEffect
的deps)并重新渲染钩子,以便我们可以测试案例:如果exp
被更改。
useTokenExpirationCheck.ts
:
import useEffect from 'react';
import useDispatch from 'react-redux';
const logout = () => ( type: 'LOGOUT' );
export default function useTokenExpirationCheck(exp: number): void
const dispatch = useDispatch();
useEffect(() =>
if (exp)
console.log('exp: ', exp);
const timeToLogout = exp * 1000 - Date.now();
setTimeout(() =>
dispatch(logout());
, timeToLogout);
, [exp]);
useTokenExpirationCheck.test.tsx
:
import renderHook from '@testing-library/react-hooks';
import React from 'react';
import Provider from 'react-redux';
import createMockStore from 'redux-mock-store';
import useTokenExpirationCheck from './useTokenExpirationCheck';
describe('useTokenExpirationCheck', () =>
test('should dispatch logout action after delay', async () =>
let exp = 6000;
jest.spyOn(Date, 'now').mockReturnValue(5900 * 1000);
const mockStore = createMockStore([]);
const store = mockStore();
jest.useFakeTimers();
const rerender = renderHook(() => useTokenExpirationCheck(exp),
wrapper: ( children ) => <Provider store=store>children</Provider>,
);
jest.advanceTimersByTime(100 * 1000);
expect(store.getActions()).toEqual([ type: 'LOGOUT' ]);
exp = 6100;
rerender();
jest.advanceTimersByTime(200 * 1000);
expect(store.getActions()).toEqual([ type: 'LOGOUT' , type: 'LOGOUT' ]);
);
);
测试结果:
PASS examples/69967414/useTokenExpirationCheck.test.tsx (8.329 s)
useTokenExpirationCheck
✓ should pass (39 ms)
console.log
exp: 6000
at examples/69967414/useTokenExpirationCheck.ts:10:15
console.log
exp: 6100
at examples/69967414/useTokenExpirationCheck.ts:10:15
------------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------------------|---------|----------|---------|---------|-------------------
All files | 100 | 50 | 100 | 100 |
...nExpirationCheck.ts | 100 | 50 | 100 | 100 | 9
------------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 8.885 s, estimated 10 s
Ran all test suites related to changed files.
【讨论】:
slideshowp2,这太棒了!我使用的是 Redux 工具包而不是 redux,所以我将类型更改为配置文件/注销,(按照工具包切片工作流程),但基本上我根据您的建议进行了更改,它终于可以工作了!我花在“测试”这件作品上的时间比花在钩子本身上的时间要多得多……谢谢阿根♥以上是关于使用 SetTimeout 测试自定义钩子,使用 Jest 测试 useEffect的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 JEST、Enzyme 在 React 中测试自定义钩子?
为啥 jest 不能为我正在测试的自定义 React 钩子提供 useTranslation 钩子?
如何使用 react-hooks-testing-library 测试自定义 async/await 钩子