测试自定义挂钩并获得“警告:测试中对TestHook的更新未包含在行为中

时间:2019-06-15 16:01:40

标签: reactjs unit-testing react-hooks react-hooks-testing-library

编辑:我找到了一个解决方案,来自Kent C. Dodds的this video

只需将jest.advanceTimersByTime调用包装在act函数中即可。

所以这个:

jest.advanceTimersByTime(510);

成为:

act(()=>jest.advanceTimersByTime(510));

我已经实现了一个简单的自定义钩子,以便对值更新进行反跳操作。

代码如下:

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return debouncedValue;
}

这个钩子的客户端是这样的(非常简化):

function Search(props) {
  const [searchTerm, setSearchTerm] = useState('');
  const debouncedSearchTerm = useDebounce(searchTerm, 500);

  // doing something useful with debouncedSearchTerm....
  // ...
  // ...
}

因此,我正在尝试使用以下代码测试该钩子:

import { renderHook, act } from 'react-hooks-testing-library';
import useDebounce from '../useDebounce';

jest.useFakeTimers();

it.only('should update value after specified delay', () => {
  const { result, rerender } = renderHook(
    ({ value, delay }) => useDebounce(value, delay),
    { initialProps: { value: '', delay: 500 } }
  );

  expect(result.current).toBe('');
  jest.advanceTimersByTime(510);
  expect(result.current).toBe('');

  rerender({ value: 'Hello World', delay: 500 });

  expect(result.current).toBe('');
  jest.advanceTimersByTime(498);
  expect(result.current).toBe('');
  jest.advanceTimersByTime(3);
  expect(result.current).toBe('Hello World');
});

尽管测试通过了,但我收到以下警告:

console.error node_modules/react-test-renderer/cjs/react-test-renderer.development.js:102

Warning: An update to TestHook inside a test was not wrapped in act(...).        When testing, code that causes React state updates should be wrapped into act(...):        act(() => {      /* fire events that update state */    });    /* assert on the output */        This ensures that you're testing the behavior the user would see in the browser. Learn more at.....       in TestHook        in Suspense

我知道,如果我要调用一个函数以更新挂钩的内部状态(例如useCounter挂钩的增量()),则必须在 act 功能,如文档所述。

但是,我实现的useDebounce挂钩通过内部useEffect更改状态,该useEffect在值或延迟更改时运行。

如何摆脱这种警告?我的代码有问题吗?我忘了在测试代码中添加一些内容吗?

请帮助!

1 个答案:

答案 0 :(得分:0)

将此行包裹到 act 中:

it.only('should update value after specified delay', () => {
  const { result, rerender } = renderHook(
    ({ value, delay }) => useDebounce(value, delay),
    { initialProps: { value: '', delay: 500 } }
  );

  expect(result.current).toBe('');
  jest.advanceTimersByTime(510);
  expect(result.current).toBe('');

  rerender({ value: 'Hello World', delay: 500 });

  expect(result.current).toBe('');
  jest.advanceTimersByTime(498);
  expect(result.current).toBe('');
  // once delay time is passed, component is rerendered, so to capture that
  // in unit test, this line should be wrapped in act
  act(() => jest.advanceTimersByTime(3));
  expect(result.current).toBe('Hello World');
});```