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

时间:2017-07-28 15:12:53

标签: reactjs mocha 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;

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

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

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

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

非常感谢!

2 个答案:

答案 0 :(得分:3)

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

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

关于您的特定代码,滚动侦听器在componentDidMount中设置,但您的组件呈现浅,因此实际上不会调用componentDidMount(因此没有侦听器)。尝试将此行添加到beforeEachMyTestComponent.instance().componentDidMount()

答案 1 :(得分:1)

你可以说事件只是简单的消息 - 因为酶正在使用JSDOM,你可以充分跟踪这些消息,因为它们使用普通的javascript连接到节点,无论事件是否是&#39;滚动&#39;,&#39; foo&#39;或&#39; bar&#39;。

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

以下是跟踪非合成事件的示例,例如使用酶滚动:

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

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

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()
  })
})

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

这里有一些JSDOM的限制,因为如果你需要做一些事情,比如跟踪大小或高度,或事件触发后滚动节点,你就不走运了 - 这是因为JSDOM没有&#39 ; t实际上为您呈现页面,而不是模仿&#39;用于像酶这样的libs的DOM - 也可以说有这些需求的测试更适合端到端测试和完全不同的工具。