Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Test React Hooks useEffect, useCallBack

I'm trying to write unit test cases using Jest, Enzyme for useEffect, and useCallback for React hooks but I'm unable to succeed. Can you someone help me to write a test case for the below code.

ModalComponent.jsx

  const ModalComponent = ({ closeModal }) => {
     const handleModal = useCallback((event) => {
        if (event.keyCode === 27) {
          closeModal(false);
        }
     }
     useEffect(() => {
        document.addEventListener('keydown', handleModal);
        return () => document.removeEventListener('keydown', handleModal);
     }, []);

     return (
        <Modal>
          <Header onClose={closeModal} />
          <Body />
          <Footer />
        </Modal>
     );
  }

ModalComponent.spec.jsx

  describe('Modal Component', () => {
     let props;
     beforeEach(() => {
       props = {
         closeModal: jest.fn(),
       };
     };

     it('should handle useEffect', () => {
        jest.spyOn(React, 'useEffect').mockImplementation(f => f());
        document.addEventListener('keydown', handleModal); 
        document.removeEventListener('keydown', handleModal);
        const component = shallow(<ModalComponent />);
     });
  });

It is unable to cover these lines document.addEventListener('keydown', handleModal);,document.removeEventListener('keydown', handleModal);, if(event.keyCode === 27), closeModal(false). How can I cover the test cases?

like image 420
Kumar Pranay Avatar asked Nov 02 '25 02:11

Kumar Pranay


1 Answers

React internals shouldn't be mocked unless necessary because this results in synthetic tests that don't conform to the way the framework works and give false positives. This especially applies to hooks like useEffect because they have hidden state and may not work as a tester expects.

React functional components don't expose component instance and supposed to be tested by asserting the result. Tests can be strengthened up with spy assertions to make results less ambiguous.

Since listeners are set on document, it needs a focus, something like:

jest.spyOn(document, 'addEventListener');
jest.spyOn(document, 'removeEventListener');
const onCloseSpy = jest.fn();
const component = mount(<ModalComponent closeModal={onCloseSpy} />);
expect(component.find(Header).prop('onClose')).toBe(onCloseSpy);

expect(document.addEventListener).toBeCalledTimes(1);
expect(document.addEventListener).toBeCalledWith('keydown', expect.any(Function));

document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 37}));
expect(onCloseSpy).not.toBeCalled();
document.dispatchEvent(new KeyboardEvent('keydown', {keyCode: 27}));
expect(onCloseSpy).toBeCalledWith(false);


// rerender to make sure listeners are set once
component.setProps({});    
expect(document.addEventListener).toBeCalledTimes(1);
expect(document.removeEventListener).not.toBeCalled();

// unmount
component.unmount();
expect(document.removeEventListener).toBeCalledTimes(1);
const [, callback] = document.addEventListener.mock.calls[0];
expect(document.removeEventListener).toBeCalledWith('keydown', callback);
like image 168
Estus Flask Avatar answered Nov 04 '25 19:11

Estus Flask



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!