Jest/Enzyme测试中如何移除事件监听器

6

学习使用jest和enzyme来测试使用create-react-app引导的react应用程序。

使用unmount模拟add/removeEventListener在shallow后崩溃,并在mount后使用unmount时出现警告,见下文。有人知道我做错了什么吗?

我的测试:

it('should add and remove resize event handler', () => {
  const adder = jest
    .spyOn(global, 'addEventListener')
    .mockImplementation(() => {});
  const remover = jest
    .spyOn(global, 'removeEventListener')
    .mockImplementation(() => {});
  const wrapper = shallow(<App />);
  // this seems to work
  expect(adder).toHaveBeenCalled();
  // causing issues
  wrapper.unmount();
  expect(remover).toHaveBeenCalled();
});

mount 后使用 unmount

  console.error node_modules/fbjs/lib/warning.js:33
    Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.

    Please check the code for the App component.

shallow 之后使用 unmount:

/home/nik/projects/learn/jest/node_modules/react-scripts/scripts/test.js:20
  throw err;
  ^

Invariant Violation: ReactShallowRenderer render(): Invalid component element.
    at invariant (/home/nik/projects/learn/jest/node_modules/fbjs/lib/invariant.js:42:15)
    at ReactShallowRenderer.render (/home/nik/projects/learn/jest/node_modules/enzyme-adapter-react-16/node_modules/react-test-renderer/cjs/react-test-renderer-shallow.development.js:104:38)
    at Updater.enqueueSetState (/home/nik/projects/learn/jest/node_modules/enzyme-adapter-react-16/node_modules/react-test-renderer/cjs/react-test-renderer-shallow.development.js:329:20)
    at App.Object.<anonymous>.Component.setState (/home/nik/projects/learn/jest/node_modules/react/cjs/react.development.js:237:16)
    at loadData.then.results (/home/nik/projects/learn/jest/src/App.js:15:12)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
error An unexpected error occurred: "Command failed.
Exit code: 1
Command: sh
Arguments: -c react-scripts test --env=jsdom
Directory: /home/nik/projects/learn/jest
Output:
".
info If you think this is a bug, please open a bug report with the information provided in "/home/nik/projects/learn/jest/yarn-error.log".
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

你能发布 App 的源代码吗? - mpontus
1
请看这里:https://github.com/nikrb/cra-jest-play/blob/jest-fetch-save/src/App.js - nikrb
2个回答

1

我在 Promise 中包装了卸载操作。

  return Promise.resolve().then(() => {
    wrapper.unmount();
    expect(remover).toHaveBeenCalled();
  });

您也错过了一个错误提示中的线索,即“loadData”没有被模拟:

  jest.spyOn(App.prototype, 'loadData')
    .mockImplementation(() => {
      return new Promise(resolve => resolve([topics, subTopics]))
    });

0
为了解决第一个问题,您必须采取措施,在卸载组件后避免调用 setState
更简单的方法是通过维护 _isMounted 标志,在调用 setState 之前进行检查:
componentDidMount() {
  this._isMounted = true;

  this.loadData().then(results => {
    if (!this._isMounted) {
      return;
    }

    const [topics, subTopics, ...rest] = results;

    this.setState({ topics, subTopics });
  });

  window.addEventListener('resize', this.someHandler);
}

componentWillUnmount() {
  this._isMounted = false;

  window.removeEventListener('resize', this.someHandler);
}

您可以在React博客中阅读更多关于此问题的信息,并找到其他解决方案:https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html

抱歉,我不喜欢你的解决方案...但这让我想到为什么我在...Unmount中调用了setState。我想也许我调用了unmount太早了,所以我将其包装在一个return Promise.resolve().then()中,它似乎可以工作,尽管我无法相信我之前没有尝试过这个方法。 - nikrb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接