How to unit test useEffect cleanUp return function using Jest and Enzyme

Viewed 10806

I tried many scenarios but none worked, am facing issues with useEffect Cleanup return method. Looking for some solutions to cover the use case. Not able to attach the screenshot because of less reputation.

I have did some research and followed the solutions provided like creating spyOn, mount, unMount scenarios. But none worked.

useEffect(() => {
    window.addEventListener('scroll', handleScroll);
    window.addEventListener('resize', handleResize);
    return () => {
       window.removeEventListener('scroll', handleScroll);
       window.removeEventListener('resize', handleResize);
    };
}, []);

Expecting test coverage for return statement inside useEffect function.

Please excuse typos - posted from mobile.

6 Answers

I would suggest you checkout react-testing-library. The library offers a unmount method, that gets returned from its render method. After calling unmount(), you can check if the listeners have been removed. In general, I can very much recommend react-testing-library. We use it for all our frontend tests in combination with jest and it works like a charm.

Sample usage of the unmount method:

import { render } from "@testing-library/react";

it("Should do what I want", () => {
  const { unmount } = render(
    <MyComponent value="test" } />
  );

  unmount();

  // ... expects
});

In order to run the clean up function you specified in the useEffect hook, you can cache a reference to it and then call that reference later in your test:

let cleanupFunc;

jest.spyOn(React, "useEffect").mockImplementationOnce(func => {
    cleanupFunc = func();
});

cleanupFunc();

Here is the complete answer that solves my problem:

it('Should run useEffect cleanUp return function', () => {
  const remover = jest
    .spyOn(global, 'removeEventListener')
    .mockImplementation(() => {});
  act(() => {
    component = mount( < MyComponent value = "Test" / > );
  });
  component.unmount();

  expect(remover).toHaveBeenCalled();
});

To those of you who, like me, could not make the accepted answer work:

jest.spyOn(React, "useEffect").mockImplementationOnce(cb => cb()());

Adding the second () to the callback worked to execute the cleanup.

Building on the answer from @thomas-rufflo it's worth pointing out that:

import { useEffect } from 'react';

Doesn't work. You have to use:

React.useEffect

Taken from this blog post: https://blog.carbonfive.com/shallow-testing-hooks-with-enzyme/

I'm new to front-end testing so this tripped me up for some time!

React doesn't run the hook in sync mode. https://codesandbox.io/s/m5m5yqrr18

import React from "react";
import { cleanup, render } from "react-testing-library";

afterEach(cleanup);

it("React test", () => {
  let a = false;
  function Test() {
    React.useEffect(() => {
      a = true;
    });
    return <div>Hello World</div>;
  }
  const { rerender } = render(<Test />);
  rerender(<Test />);
  expect(a).toBe(true);
});

Related