Testing useEffect unmount callback called using acts in enzyme

Viewed 4506

I have a component with this kind of setup

const ChestWithToys = ({toyBoxId, handleTidyToys})=> {
    /* do stuff with toys */
    useEffect(() => () => handleTidyToys(), [toyBoxId])
}

And this is in my test with enzyme and act (from react-dom/test-utils)

it('tidies toys on unmount', () => {
    let handleTidyToys = sinon.stub();
    let wrapper = shallow(<ChestWithToys toyBoxId={1} handleTidyToys={handleTidyToys} />);
    expect(handleTidyToys).to.have.callCount(0);
    act(() => {
       wrapper.unmount();
    });
    expect(handleTidyToys).to.have.callCount(1);
});

The component functions as expected. However in the test handleTidyToys is never called. What gives? I can't find a better example where unmounting takes place, but from what I can see that should trigger my cleanup callback.

If I replace the unmount with setProps and change the queryId, it still doesn't trigger.

Is enzyme incompatible, or have I made some other mistake?

edit: I've just dropped a load of console.logs into the useEffect, and around it and it's not running at all.

1 Answers

This is an approach that worked for me, with some caveats:

  1. I've used jest for spying and mocking instead of sinon;
  2. I've used react-dom's render function instead of enzyme shallow - this is a bit of cheating as render is probably similar to full mount:
import { act } from 'react-dom/test-utils';
import { render, unmountComponentAtNode } from 'react-dom';
// import your ChestWithToys component

let useEffect, container;

beforeEach(() => {
  // setup a DOM element as a render target
  container = document.createElement('div');
  document.body.appendChild(container);
});

afterEach(() => {
  // cleanup on exiting
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it('tidies toys on unmount', () => {
  let handleTidyToys = jest.fn();
  useEffect = jest.spyOn(React, "useEffect");

  act(() => {
    render(<ChestWithToys toyBoxId={1} handleTidyToys={handleTidyToys} />, container);
  });

  // same as expect(handleTidyToys).to.have.callCount(0);
  expect(handleTidyToys).not.toHaveBeenCalled();

  unmountComponentAtNode(container);
  // same as expect(handleTidyToys).to.have.callCount(1);  
  expect(handleTidyToys).toHaveBeenCalled();
});
Related