Reputation: 49
I'm trying to test a relatively simple custom hook that uses useEffect and setTimeout. However, my test fails, and I cannot figure out what is wrong.
Here is the hook itself(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]);
}
and my testing file:
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);
});
});
What I know is that the exp variable is somehow not passed to the useTokenExpirationCheck function (console.log showed me 0 inside of the function, when it was executed). So basically, I am not even getting to the useEffect itself... any ideas what could go wrong?
Upvotes: 3
Views: 3422
Reputation: 102207
Here is my testing strategy:
The mock store will create an array of dispatched actions which serve as an action log for tests.
So that I can get and assert the dispatched actions by store.getActions()
method.
I will mock Date.now()
method with a mocked return value so that the tests don't rely on system date anymore. System dates may be different for different timezone and different CI/CD servers.
Use Props to update the inputs(deps of useEffect
) and rerender the hook so that we can test the case: If exp
is changed.
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' }]);
});
});
test result:
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.
Upvotes: 2