Reputation: 308
Trying to test Custom useInterval Hook but jest.advanceTimersByTime(199);
and jest.advanceTimersToNextTimer(1);
don't seem to be working.
I log jest.getTimerCount()
anywhere and it returns 0;
Custom Hook:
import { useRef, useEffect } from 'react';
function useInterval(callback: () => void, delay: number | null) {
const savedCallback = useRef<() => void | null>();
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
});
// Set up the interval.
useEffect(() => {
function tick() {
console.log("here"); // This never gets logged !!!!
if (typeof savedCallback?.current !== 'undefined') {
console.log(delay, savedCallback);
}
}
if (delay !== null) {
const id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
export default useInterval;
Test:
import useInterval from "./useInterval";
import { renderHook } from '@testing-library/react-hooks';
describe("useInterval Hook:", () => {
let callback = jest.fn();
beforeAll(() => {
// we're using fake timers because we don't want to
// wait a full second for this test to run.
jest.useFakeTimers();
});
afterEach(() => {
callback.mockRestore();
jest.clearAllTimers();
});
afterAll(() => {
jest.useRealTimers();
});
test('should init hook with delay', () => {
const { result } = renderHook(() => useInterval(callback, 5000));
expect(result.current).toBeUndefined();
expect(setInterval).toHaveBeenCalledTimes(1);
expect(setInterval).toHaveBeenCalledWith(expect.any(Function), 5000);
});
test('should repeatedly calls provided callback with a fixed time delay between each call', () => {
const { result } = renderHook(() => useInterval(callback, 200));
expect(callback).not.toHaveBeenCalled();
// fast-forward time until 1s before it should be executed
jest.advanceTimersByTime(199);
expect(callback).not.toHaveBeenCalled(); // FAILS
// jest.getTimerCount() here returns 0
// fast-forward until 1st call should be executed
jest.advanceTimersToNextTimer(1);
expect(callback).toHaveBeenCalledTimes(1);
// fast-forward until next timer should be executed
jest.advanceTimersToNextTimer();
expect(callback).toHaveBeenCalledTimes(2);
// fast-forward until 3 more timers should be executed
jest.advanceTimersToNextTimer(3);
expect(callback).toHaveBeenCalledTimes(5);
});
});
Upvotes: 1
Views: 2502
Reputation: 533
I struggled with this for hours until finally I found this article. https://overreacted.io/making-setinterval-declarative-with-react-hooks/
Just copy the useInterval function verbatim, and then use it with the syntax also provided. It just works correctly with no fuss.
Upvotes: 0
Reputation: 308
I solved it by moving jest.useFakeTimers();
to the beforeEach
block instead of beforeAll
.
Upvotes: 2