web-dev-qa-db-ja.com

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を呼び出します(ここでのユースケースは、ユーザーがページ上の特定のポイントを超えてスクロールしたときに、スティッキーなナビゲーションバーを表示することだとします)。

問題は...これをテストする適切な方法を見つける必要があります。私の最終目標は、myScrollMethodプロップを介してコンポーネントに渡されるスパイメソッドを作成し、スクロールをトリガーして(またはテストでスクロールを模擬して)、最後にスクロールハンドラーメソッドが起動したかどうかをアサートすることです。これが私の試みです:

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

私が抱えている問題は、スパイが呼び出されないため、最後のアサーションが失敗することです。このサイトの他の投稿をいくつか参照しましたが、これまでのところ適切な解決策は見つかりませんでした。しばらく頭を悩ませてきたので、どんな提案でも大歓迎です!

どうもありがとう!

6
Jamie Bradley

残念ながら、ここでEnzymeが大いに役立つとは思いません。ライブラリは、Reactの合成イベントシステム内のイベントのみを処理します。したがって、Enzymeでレンダリングしたコンポーネントは、ウィンドウに追加されたイベントリスナーでは機能しません。 これはEnzymeのgithubのスレッドを発行します は詳細を提供し、あなたに役立つかもしれないいくつかの推奨される回避策があります。

たとえば、_window.addEventListener_をスパイしたい場合は、マウント時に、引数_"scroll"_とコールバックで呼び出されたことを確認できます。

特定のコードに関しては、スクロールリスナーはcomponentDidMountに設定されていますが、コンポーネントは浅くレンダリングされるため、componentDidMountは実際には呼び出されません(リスナーはありません)。次の行をbeforeEachに追加してみてください:MyTestComponent.instance().componentDidMount()

4
boylingpoynt

あなたはイベントが単なるメッセージである場合を作ることができます-酵素はフードの下でJSDOMを使用しているので、イベントが「スクロール」、「foo」であるかどうかに関係なく、プレーンなJavaScriptを使用してノードに接続されているので、これらのメッセージを適切に追跡できます、または「バー」。

テスト環境では、イベントが呼び出されても問題ありません。システムは、イベントへの応答方法を知っている必要があります。

酵素を使ったスクロールのような非合成イベントを追跡する例を次に示します。

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

イベントをdomにアタッチした後、 getDOMNode および dispatchEvent を使用してハンドラーをトリガーできます。これにより、prop onScrollが起動します

ここでJSDOMにはいくつかの制限があります。たとえば、サイズまたは高さを追跡する必要がある場合、またはイベントが発生した後にノードのscrollTopを実行する必要がある場合、運が悪くなります。これは、JSDOMが実際にレンダリングしないためですページの代わりに、酵素のようなlibsを使用するためにDOMを「エミュレート」します。これらのニーズを備えたテストは、エンドツーエンドのテストと完全に異なるツールに適しているとも言えます。

2
lfender6445