react-hooks-testing-library: Spy on console.error doesn't work

  • react-hooks-testing-library version: 5.0.3
  • react version: 16.13.1
  • node version: 14.15.4

Relevant code or config:

    it('should abort request if component is unmounting', async () => {
      // Given
      const consoleErrorSpy = jest.spyOn(global.console, 'error');
      const { result, unmount } = renderHook(() => useFetch('key', fetchFn));

      // When
      unmount();

      // Then
      expect(fetchFn).toHaveBeenCalledTimes(1);
      expect(result.current.status).toBe('pending');
      expect(consoleErrorSpy).not.toHaveBeenCalled();
    });

What you did:

I try to make a test failed when I get a React warning. For example, I run a race condition in my source code and I get the following error in console : Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

What happened:

This test always passed, even if the console.error is shown in the terminal so it’s a false positive test.

Capture d’écran 2021-01-25 à 16 40 31

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 1
  • Comments: 26 (14 by maintainers)

Most upvoted comments

Asserting something didn’t occur is always tough because there is other way (that I’m aware of) than just waiting long enough for that you’re confident an update would have happened if it was going to happen. That’s what this solution does. It sets up an update to occur in 200ms, then it unmounts the component and waits for 250ms, then check that no logs were produced.

If an update happens (can only occur if the component failed to unmount for some reason - I’m not sure if that’s even possible), waitForNextUpdate wont reject, so that assertion will fail. If and update was attempted on the unmounted component, the log will be produced at around 200ms and waitForNextUpdate will reject at around 250ms, passing that assertion but failing the next one checking console.error spy.

It might have been possible to get this test working with a fake timer, but In general I don’t like faking timers. I’ve seen more cases where the tests get harder to read or become fiddly to get passing than I’ve seen the extra 250ms of test runtime become an issue, plus it gives me more confidence that it’s going to work in the real world where time can’t be controlled by calling a function.

My mistake, I was forget the consoleSpyError expect just after the waitForNextUpdate. This solution is very acceptable, thank you, that is what I want to achieve.

Async utils are very difficult to understand. I’m not sure to understand completely the solution anyway but you teach me a new way to handle timers with RTL.

That is not what I’m seeing…

Flag commented out: image

Flag left in: image

This is with my waitForNextUpdate version. replacing the test with your waitForValueToChange version does produce the false positive.

@ludovicmnji please note that in my example, I had removed the jest.useFakeTimers("modern"); line from the test (so the timeout would fire itself).

Yes, I have a pending state and have an expect dedicated for this. I didn’t write in the example to be more focus on console.error issue.

the console.error isnt an issue, so it’s really hard to help you write your test if all the bits aren’t there to actually write the test for.

There are loads of examples in closed issues how to tackle what you want to do, here’s one: https://github.com/testing-library/react-hooks-testing-library/issues/425 this person uses manual mocks you could mock react’s useReducer function & test that dispatch does not fire. This would be more reliable.

I don’t think sandbox is the best place for testing, it seems like mock & useFakeTimers don’t work. see here – https://github.com/codesandbox/codesandbox-client/issues/513. So, i’ve redone the sandbox in a repo here – https://github.com/joshuaellis/rhtl-555 and the test passes with no errors.

Maybe i’m missing something?