【问题标题】:How to Test React Hooks useEffect, useCallBack如何测试 React Hooks useEffect, useCallBack
【发布时间】:2020-11-01 11:39:18
【问题描述】:

我正在尝试使用 Jest、Enzyme for useEffect 和 useCallback for React hooks 编写单元测试用例,但我无法成功。你能帮我为下面的代码写一个测试用例吗?

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 />);
     });
  });

无法覆盖document.addEventListener('keydown', handleModal);document.removeEventListener('keydown', handleModal);if(event.keyCode === 27)closeModal(false) 这些行。如何覆盖测试用例?

【问题讨论】:

  • Hooks 不是为了测试实现而设计的。测试行为。
  • @EstusFlask,我能够通过模拟 useEffect 和 useCallback 得到这个测试用例。 jest.spyOn(React, 'useEffect').mockImplementation(f =&gt; f());
  • 有可能,但这不是通常的做法。你不应该在没有充分理由的情况下模拟框架本身,这可能会导致纯粹的合成测试不符合现实世界的期望。这尤其适用于钩子。测试这一点的正确方法是触发 keydown 事件,或至少 spy/mock document 方法并断言它们的调用。
  • @EstusFlask 我该怎么做。如果你能提供一个非常有用的例子。我提到了您在上述评论中提到的链接,他们找到了元素并模拟了类似 wrapper.find('input').simulate('keypress', {key: 'Enter'}) 的事件,但在我的情况下,我无法将输入或任何其他元素传递给 find 方法,所以你能指导我吗得到这份工作?

标签: reactjs jestjs react-hooks enzyme


【解决方案1】:

除非必要,否则不应嘲笑 React 内部,因为这会导致合成测试不符合框架的工作方式并给出误报。这尤其适用于像 useEffect 这样的钩子,因为它们具有隐藏状态并且可能无法按测试人员的预期工作。

React 功能组件不暴露组件实例,并且应该通过断言结果来进行测试。可以使用 spy 断言来加强测试,以减少结果的模棱两可。

由于侦听器设置在document,它需要一个焦点,例如:

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);

【讨论】:

  • 我在我的机器上试过这个测试用例。下面的行失败了,但是它根本无法覆盖 useEffect() 。 expect(document.addEventListener).toBeCalledTimes(1);
  • 我明白了。 shallow 一直存在问题,需要像你所做的那样修补 useEffect,github.com/enzymejs/enzyme/issues/2086,这是一个很大的障碍。我建议此时使用带有mount 的深度渲染,您可以存根嵌套组件,以防您不希望它们干扰测试。或者,试试github.com/mikeborozdin/jest-react-hooks-shallow
猜你喜欢
  • 2020-02-15
  • 1970-01-01
  • 2020-05-07
  • 1970-01-01
  • 2020-12-28
  • 2021-04-15
  • 1970-01-01
  • 2020-03-20
  • 2020-11-07
相关资源
最近更新 更多