Testing useInterval hook with jest does not register timer

Viewed 300

I know there are many other questions regarding setInterval and jest. I've read all that I could find, I've read the docs and I think the following should work, but it does not:

// useInterval.js
import React, { useEffect } from "react";

/**
 * Calls function in an interval. Unregisters the interval when component is unmounted
 * @param {function} callback the function to be executed
 * @param {Array} dependencyArray the dependency array you would pass to useEffect(because that's what this hook is). Be very careful about this, unfor
 * @param {number} intervalInMs
 * @param {boolean}} runImmediately if the function should run immediately as well. if false, the function will only run after intervalInMs has passed
 */
const useInterval = (
  callback,
  dependencyArray,
  intervalInMs,
  runImmediately = true
) => {
  useEffect(() => {
    // schedule interval
    const intervalHandle = setInterval(callback, intervalInMs);
    // run function immediately
    if (runImmediately) {
      callback();
    }
    return () => clearInterval(intervalHandle);
  }, [...dependencyArray, intervalInMs]);
};

export default useInterval;
//useInterval.test.js
import { renderHook, act } from "@testing-library/react-hooks";
import useInterval from "../hooks/useInterval";

describe("useInterval hook", () => {
  let callback;
  beforeEach(() => {
    callback = jest.fn();
  });

  beforeAll(() => {
    jest.useFakeTimers();
  });

  afterEach(() => {
    callback.mockRestore();
    jest.clearAllTimers();
  });

  afterAll(() => {
    jest.useRealTimers();
  });

  it("calls callback repeatedly", async () => {
    const { result } = renderHook(() => useInterval(callback, [], 15));

    expect(result.current).toBeUndefined();
    expect(setInterval).toHaveBeenCalledTimes(1);
    expect(callback).toHaveBeenCalledTimes(1);
    expect(jest.getTimerCount()).toEqual(1); // nope, the test fails with expected: 1 received: 0

    jest.advanceTimersByTime(10);
    expect(callback).toHaveBeenCalledTimes(1); 

    jest.advanceTimersByTime(5);

    expect(callback).toHaveBeenCalledTimes(2); // if getTimerCount is commented out, this fails with expected: 2 received: 1
  });
});

I've also tried wrapping the jest.advanceTimersByTime into act: act(()=>jest.advanceTimersByTime(10)) but this does not change the output.

Is there something I'm missing about this? The hook does work when used in a component.

0 Answers
Related