Skip to content

Instantly share code, notes, and snippets.

@bigsaigon333
Created August 19, 2021 10:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bigsaigon333/e72c71d316fabfb03c53702f85be7e97 to your computer and use it in GitHub Desktop.
Save bigsaigon333/e72c71d316fabfb03c53702f85be7e97 to your computer and use it in GitHub Desktop.
useInterval hook 테스트하기
import { renderHook } from "@testing-library/react-hooks/dom";
import useInterval from "./useInterval";
jest.useFakeTimers();
jest.spyOn(globalThis, "setInterval");
jest.spyOn(globalThis, "clearInterval");
afterEach(() => {
jest.clearAllMocks();
});
describe("useInterval", () => {
test("delay 가 null 인 경우, callback은 호출되지 않는다", () => {
const callback = jest.fn();
renderHook(() => useInterval(callback, null));
jest.runAllTimers();
expect(setInterval).not.toHaveBeenCalled();
});
test("delay ms 간격으로 callback이 주기적으로 호출된다", () => {
const callback = jest.fn();
const delay = 10_000;
renderHook(() => useInterval(callback, delay));
expect(setInterval).toHaveBeenCalledWith(expect.any(Function), delay);
jest.advanceTimersByTime(delay);
expect(callback).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(delay);
expect(callback).toHaveBeenCalledTimes(2);
});
test("callback 함수가 변경되어도 delay ms 간격으로 callback이 호출된다", () => {
const callback = jest.fn();
const delay = 15_000;
const initialProps = { callback, delay };
const { rerender } = renderHook<typeof initialProps, void>(
(props) => useInterval(props.callback, props.delay),
{ initialProps }
);
// useInterval 이 실행된 직후 setInterval이 1회 호출된다
expect(setInterval).toHaveBeenCalledTimes(1);
// delay/2 ms 가 지난 이후에 어떤 이유로 useInterval 이 재호출되며 callback 의 참조값이 변경되었다
const newCallback = jest.fn();
jest.advanceTimersByTime(delay / 2);
rerender({ callback: newCallback, delay });
// callback이 변경되어도 clearInterval(useEffect cleanup function)과 setInterval은 호출되지 않는다.
expect(clearInterval).not.toBeCalled();
expect(setInterval).toHaveBeenCalledTimes(1);
// 추가로 delay/2 ms 가 지나, setInterval의 interval만큼 경과되어 newCallback이 실행된다.
jest.advanceTimersByTime(delay / 2);
expect(newCallback).toHaveBeenCalledTimes(1);
// delay/2 ms 가 지나도 newCallback은 실행되지 않는다.
jest.advanceTimersByTime(delay / 2);
expect(newCallback).toHaveBeenCalledTimes(1);
});
});
import { useEffect, useRef } from "react";
const useInterval = (callback: () => unknown, delay: number | null) => {
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
if (delay == null) {
return;
}
const timeId = setInterval(() => savedCallback.current(), delay);
// eslint-disable-next-line consistent-return
return () => clearInterval(timeId);
}, [delay]);
};
export default useInterval;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment