【问题标题】:How do I test a polling react hook using Jest with mock timers?如何使用带有模拟计时器的 Jest 测试轮询反应钩子?
【发布时间】:2022-01-06 12:49:25
【问题描述】:

我有一个投票钩子,它似乎在我的应用程序中工作。

import { useCallback, useEffect, useRef } from "react";
/**
 * @param asyncFunction
 * @param interval milliseconds between calls to the asyncFunction, defaults to a minute
 * @param immediate if true it will run the asyncFunction immediately before looping
 */
export function usePolling(
  asyncFunction: () => any | PromiseLike<any>,
  interval = 60000,
  immediate = true
) {
  const timeoutRef = useRef<ReturnType<typeof setTimeout>>();
  const mountedRef = useRef(false);
  const activeRef = useRef(false);

  const wrappedAsyncFunction = useCallback(async () => {
    console.log("POLL", { mountedRef, activeRef });
    if (!mountedRef.current || activeRef.current) {
      // don't process if currently active or the component is unmounted
      return;
    }
    activeRef.current = true;
    try {
      console.log("ABOUT TO ASYNC", { mountedRef, activeRef });
      await asyncFunction();
      console.log("ASYNC DONE", { mountedRef, activeRef });
    } catch (e) {
      console.error("Error while polling", e);
    }
    console.log("ABOUT TO SET TIMEOUT");
    timeoutRef.current = setTimeout(wrappedAsyncFunction, interval);
    console.log(" SET TIMEOUT", timeoutRef);
    activeRef.current = false;
  }, [mountedRef.current, activeRef.current, asyncFunction, interval]);

  useEffect(() => {
    mountedRef.current = true;
    if (immediate) {
      wrappedAsyncFunction();
    } else {
      timeoutRef.current = setTimeout(wrappedAsyncFunction, interval);
    }
    return () => {
      clearTimeout(timeoutRef.current!);
      mountedRef.current = false;
      activeRef.current = false;
    };
  }, []);

  return { activeRef };
}

我的测试看起来像这样

  it("should work with just the callback", async () => {
    jest.useFakeTimers();
    const callback = jest.fn();
    let renderCount = 0;
    function MyComponent() {
      usePolling(callback);
      ++renderCount;
      return (<div data-testid="test">{renderCount}</div>);
    }

    const { getByTestId } = render(<MyComponent />)
    expect(getByTestId("test").textContent).toEqual("1");
    expect(renderCount).toEqual(1);
    jest.runAllTicks();
    expect(callback).toBeCalledTimes(1);
    console.log("ABOUT TO RUN ALL TIMERS")
    jest.runAllTimers();
    console.log("RAN ALL TIMERS")
    await waitFor(() => {
      expect(callback).toBeCalledTimes(2);
      expect(renderCount).toEqual(1);
    });
    jest.runAllTimers();
    await waitFor(() => {
      expect(callback).toBeCalledTimes(3);
      expect(renderCount).toEqual(1);
    });
  })

我正在做一些断言

  1. 回调在任何超时之前被调用一次(因为它默认是immediate = true
  2. 每次调用 runAllTimers() 时,都会调用回调。
  3. 没有重新渲染,因为我没有更新任何状态

如果没有模拟计时器,它似乎可以工作

  it("should work with a real clock", async () => {
    const callback = jest.fn();
    let renderCount = 0;
    function MyComponent() {
      usePolling(callback, 500, true);
      ++renderCount;
      return (<div data-testid="test">{renderCount}</div>);
    }

    const { getByTestId } = render(<MyComponent />)
    expect(getByTestId("test").textContent).toEqual("1");
    expect(renderCount).toEqual(1);
    expect(callback).toBeCalledTimes(1);
    await waitFor(() => {
      expect(callback).toBeCalledTimes(2);
      expect(renderCount).toEqual(1);
    });
    await waitFor(() => {
      expect(callback).toBeCalledTimes(3);
      expect(renderCount).toEqual(1);
    });
  })

我也尝试过以下操作,但没有运气......

...
    expect(callback).toBeCalledTimes(1);
    jest.runAllTimers();
    jest.runAllTicks();
    jest.runAllImmediates();
    await waitFor(() => {
      expect(callback).toBeCalledTimes(2); // fails here
      expect(renderCount).toEqual(1);
    });
...

【问题讨论】:

    标签: javascript reactjs typescript jestjs


    【解决方案1】:

    显然虽然不完全确定为什么...我也需要等待第一个。即使在没有waitFor

    的情况下正确设置
      it("should work with just the callback", async () => {
        jest.useFakeTimers();
        const callback = jest.fn();
        let renderCount = 0;
        function MyComponent() {
          usePolling(callback);
          ++renderCount;
          return (<div data-testid="test">{renderCount}</div>);
        }
    
        const { getByTestId } = render(<MyComponent />)
        expect(getByTestId("test").textContent).toEqual("1");
        await waitFor(() => {
          expect(renderCount).toEqual(1);
          expect(callback).toBeCalledTimes(1);
        });
        jest.runAllTimers();
        await waitFor(() => {
          expect(renderCount).toEqual(1);
          expect(callback).toBeCalledTimes(2);
        });
        jest.runAllTimers();
        await waitFor(() => {
          expect(renderCount).toEqual(1);
          expect(callback).toBeCalledTimes(3);
        });
      })
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-07
      • 1970-01-01
      • 1970-01-01
      • 2022-09-11
      • 2020-11-11
      • 2021-02-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多