Reputation: 63
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?
Upvotes: 4
Views: 26649
Reputation: 222369
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);
Upvotes: 3