使用Enzyme测试Window Scroll事件处理程序的最佳方法是什么?

Jam*_*ley 6 mocha.js reactjs enzyme

我一直在与一个新团队合作开发一个React应用程序,并围绕为触发window.scroll事件方法的组件编写单元测试.

所以,我们以此组件为例.

import React, { Component } from 'react';

class MyComponent extends Component {
  componentDidMount() {
    window.addEventListener('scroll', this.props.myScrollMethod);
  }

  componentWillUnmount() {
    window.removeEventListener('scroll', this.props.myScrollMethod);
  }

  render() {
    return (
      <div>
        <h1>Hello MyComponent!</h1>
      </div>
    )
  };
};

export default MyComponent;
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我正在使用一个方法,该方法通过prop传递给组件并将其绑定到事件所在的窗口事件侦听器scroll.在现实世界中myScrollMethod,当用户向下滚动页面时,该组件将调用(假设此处的用例是当用户滚动超出页面上的某个点时显示粘性导航栏).

问题是......我需要找到一种合适的测试方法.我的最终目标是创建一个间谍方法,通过myScrollMethodprop 传递给组件,然后触发滚动(或在测试中模拟滚动),最后断言滚动处理程序方法是否已触发.以下是我对此的尝试:

import React from 'react';
import sinon from 'sinon';
import expect, { createSpy }  from 'expect';
import { shallow } from 'enzyme';

import MyComponent from './MyComponent';

describe('The <MyComponent /> component', () => {
  let onScroll;
  let MyTestComponent;

  beforeEach(() => {
    onScroll = createSpy();
    MyTestComponent = shallow(
      <MyComponent
        myScrollMethod={onScroll}
        />
    );
  });

  it('Should call the onScroll method when a user scrolls', () => {
    expect(onScroll).toNotHaveBeenCalled();
    window.dispatchEvent(new window.UIEvent('scroll', { detail: 0 }));
    expect(onScroll).toHaveBeenCalled();
  });
});
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是最后的断言是失败的,因为间谍从未被调用过.我已经提到了这个网站上的其他一些帖子,但到目前为止还没有找到合适的解决方案.任何建议都会受到高度赞赏,因为它已经让我的大脑暂时搁置了一段时间!

非常感谢!

小智 5

不幸的是,我认为 Enzyme 在这里不会有太大帮助。该库仅处理 React 的合成事件系统内的事件。因此,您使用 Enzyme 渲染的组件不适用于添加到窗口的事件侦听器。Enzyme 的 github 上的这个问题线程提供了更多详细信息,并且有一些建议的解决方法可能会对您有所帮助。

例如,您可能想要监视window.addEventListener,然后您可以检查 mount 是否使用参数"scroll"和您的回调调用了它。

关于您的特定代码,滚动侦听器已设置,componentDidMount但您的组件是浅渲染的,因此componentDidMount实际上并未被调用(因此没有侦听器)。尝试将此行添加到您的beforeEachMyTestComponent.instance().componentDidMount()


lfe*_*445 5

您可以认为事件只是简单的消息 - 由于酶在后台使用 JSDOM,您可以充分跟踪这些消息,因为它们使用普通的 javascript 附加到节点,无论事件是“滚动”还是“foo” ,或“酒吧”。

在测试环境中,我们并不真正关心事件被调用,系统只需要知道如何响应它。

这是跟踪非合成事件(如使用酶滚动)的示例:

// scollable.js
class Scrollable extends Component {
  componentDidMount() {
    if (this.myCustomRef) {
      this.myCustomRef.addEventListener('scroll', this.handleScroll)
    }
  }

  handleScroll = (e) => this.props.onScroll(e) 
}

// scollable-test.js
import React from 'react'
import { mount } from 'enzyme'
import Scrollable from '../Scrollable'

describe('shared/Scrollable', () => {
  it('triggers handler when scrolled', () => {
    const onScroll = jest.fn()
    const wrapper = mount(
      <Scrollable onScroll={onScroll}><div /></Scrollable>
    )
    const customEvent = new Event('scroll')
    // the first element is myCustomRef
    wrapper.first().getDOMNode().dispatchEvent(customEvent)
    expect(wrapper.prop('onScroll')).toHaveBeenCalled()
  })
})
Run Code Online (Sandbox Code Playgroud)

在我们将事件附加到 dom 之后,我们可以通过使用getDOMNodedispatchEvent来触发它们的处理程序,这会触发我们的道具onScroll

JSDOM 有一些限制,因为如果你需要做一些事情,比如跟踪大小或高度,或者在事件触发后滚动节点的滚动顶部,你就不走运了 - 这是因为 JSDOM 实际上并不渲染页面,而是“模拟”DOM 以与酶等库一起使用 - 也可以说,具有这些需求的测试更适合端到端测试、无头浏览器或完全不同的工具。