My hook is;
function useInterval() {
const ref: MutableRefObject<NodeJS.Timer | null > = useRef(null);
function set(callback: () => void, delay: number) {
ref.current = setInterval(callback, delay)
}
function clear() {
if (ref.current) {
clearInterval(ref.current)
ref.current = null
}
}
return { set, clear }
}
My test is;
it("set: This should be called 10 times", () => {
var callback = jest.fn();
jest.useFakeTimers()
const { result } = renderHook(() => hooks.useInterval())
act(() => {
result.current.set(() => { callback }, 100)
jest.advanceTimersByTime(1000);
})
expect(callback).toHaveBeenCalledTimes(10);
jest.useRealTimers()
})
renderHook() and act() come from "@testing-library/react-hooks": "^7.0.2"
The result I keep getting is 0 from my expect() call. I can't seem to figure out why.
If I just use setInterval() expect() gets the correct value
it("setInterval", () => {
var callback = jest.fn();
jest.useFakeTimers()
setInterval(callback, 100)
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalledTimes(10);
jest.useRealTimers()
})
I have tried reordering the lines in every possible logical way I can think of.
I have noticed that I get the same result with or without act(), strangely.
Adding timers: "fake" or any of its variations(modern/legacy) to jest.config.ts doesn't seem to have any effect.
Obviously, testing-library/react-hooks is somehow masking setInterval() from jest.useFakeTimers() somehow but I don't understand how and am therefore unable to achieve the result I am looking for.
A part of me thinks that my hook isn't being hit by jest.useFakeTimers() because the fake timers are not being globally replaced, but I don't know how to do this.
Also, I'm using Typescript. Not that I think that makes a difference.
You passed an anonymous function to the set method instead of the mock callback. So the macro-task queued by setInterval will call the anonymous function. That's why the assertion fails. Nothing to Jest Config, TypeScript.
e.g.
useInterval.ts:
import { MutableRefObject, useRef } from 'react';
export function useInterval() {
const ref: MutableRefObject<ReturnType<typeof setInterval> | null> = useRef(null);
function set(callback: () => void, delay: number) {
ref.current = setInterval(callback, delay);
}
function clear() {
if (ref.current) {
clearInterval(ref.current);
ref.current = null;
}
}
return { set, clear };
}
useInterval.test.ts:
import { renderHook } from '@testing-library/react-hooks';
import { useInterval } from './useInterval';
describe('70276930', () => {
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
test('should call callback interval', () => {
const callback = jest.fn();
const { result } = renderHook(useInterval);
result.current.set(callback, 100);
jest.advanceTimersByTime(1000);
expect(callback).toBeCalledTimes(10);
});
test('should clear interval', () => {
const callback = jest.fn();
const { result } = renderHook(useInterval);
result.current.set(callback, 100);
jest.advanceTimersByTime(100);
expect(callback).toBeCalledTimes(1);
result.current.clear();
jest.advanceTimersByTime(100);
expect(callback).toBeCalledTimes(1);
});
});
test result:
PASS examples/70276930/useInterval.test.ts (7.541 s)
70276930
✓ should call callback interval (16 ms)
✓ should clear interval (1 ms)
----------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------------|---------|----------|---------|---------|-------------------
All files | 100 | 50 | 100 | 100 |
useInterval.ts | 100 | 50 | 100 | 100 | 9
----------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 8.294 s, estimated 9 s
package versions:
"react": "^16.14.0",
"@testing-library/react": "^11.2.2",
"jest": "^26.6.3",