3

EDIT: I found a solution, coming from this video from Kent C. Dodds.

Just wrap the jest.advanceTimersByTime calls in an act function.

So this:

jest.advanceTimersByTime(510);

becomes:

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

I've implemented a simple custom hook in order to debounce a value update.

Here is the code:

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

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

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

  return debouncedValue;
}

The client of this hook is something like this (very simplified):

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

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

So, I'm trying to test the hook with the code below:

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');
});

Although, the test passes, I get the following warning:

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

I get that if I'm about to call a function in order to update the internal state of the hook (e.g. increment() of a useCounter hook), I have to do it in an act function, as the documentation instructs.

But, the useDebounce hook I've implemented changes state by an internal useEffect that runs whenever value or delay change.

How can I get rid of this warning? Is it something wrong with my code? Am I forgetting to add something in the test code?

Please help!

  • Possible duplicate of [Jest + react-testing-library: Warning update was not wrapped in act()](https://stackoverflow.com/questions/55181009/jest-react-testing-library-warning-update-was-not-wrapped-in-act) – helloitsjoe Jun 15 '19 at 17:10
  • I managed to solve the problem! Check this: https://github.com/mpeyper/react-hooks-testing-library/issues/97 – Antonis Providakis Jun 16 '19 at 10:51

1 Answers1

1

wrap this line into 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');
});```
olzh2102
  • 11
  • 2