web-dev-qa-db-ja.com

ユニットテストを行うときに、React)のsetStateメソッドをスタブアウトまたはスパイすることは可能ですか?

コンポーネントに次の関数があります。

method(args) {
 fetch(args)
  .then((response) => {
    this.setState({
      element: response
      error: false
    });
  })
  .catch((error) => {
    this.setState({
      error: true
    });
  });
}

私はそれの単体テストを書き込もうとしているので、カスタム応答でfetch-mockを使用してfetch呼び出しをモックアウトしました。このメソッドが呼び出されたときに状態が更新され、機能していないように見えるかどうかを確認したいと思います。私は酵素、期待、およびシノンを使用していますが、component.setState = sinon.spyを設定したり、それをスタブしたりしたときに、スパイが呼び出されるようにできませんでした。 setStateが特定の要素で呼び出されたかどうか、またはユニットテストへのアプローチが間違っているかどうかを確認するにはどうすればよいですか?

10
intuition17

コンポーネントをインスタンス化する前に、プロトタイプでsetStateをスパイ/スタブアウトする必要があるようです。インスタンスのsetStateメソッドをスパイするだけでは機能しないという同様の問題が発生していました。 https://medium.com/@tjhubert/react-js-testing-setstates-callback-907df1fe720d に基づいて、sinonのアプローチは次のとおりです。

Component.js

...
method {
  this.setState({property:'value'})
}
...

Component.test.js

...
const setState = sinon.stub(Component.prototype, ‘setState’);
const component = shallow(<Component />);
component.instance().method();
expect(setState).to.be.calledOnce;
...

注:私のユースケースはジャスミンを使用しており、動作をテストするためにレンダリングを避けているため、私が知っているソリューションは次のようになります:

Component.prototype.setState = jasmine.createSpy();
const sut = new Component();
sut.method();
expect(sut.setState).toHaveBeenCalledWith({property:'value'});
4
Will

コンポーネントがマウントされ、メソッドがスタブされたときにメソッドが呼び出されると仮定して、次のことを試してください。

it('should update state with response', () => {
  const wrapper = mount(<Component />);

  return Promise.resolve().then(() => {
    expect(wrapper.state('error')).to.be.false;
  });
});

Promiseを返すことで、setTimeoutでコールバックを使用するよりもハックの少ない方法で非同期動作をテストできます。

私はスタブにsinonを使用しているので、テストでは次のようなものがあります。

sinon.stub(window, 'fetch').resolves(mockedResponse);
1
nevace

次の方法でsetStateをスタブ化できました。

let setStateStub = sinon.stub(prototype, 'setState')

また、スタブを使用した後は、元のファイルをrestore()する必要があります。これを行うには、スタブの後に以下を追加します。

setStateStub.restore()

:)

0
zero_cool

コンポーネントのsetState()メソッドを一時的にモック/置換して、次の実行を待つことができます。このような何かがトリックを行うかもしれません:

function onNextSetState(component) {
  const instance = component.instance()
  const originalSetState = instance.setState.bind(instance)
  return new Promise(resolve => {
    instance.setState = (state, callback) => {
      originalSetState(state, (...args) => {
        instance.setState = originalSetState
        resolve()
        if (callback) {
          callback(...args)
        }
      }  
    }
  }
}

it('should update state with response', async () => {
  const component = mount(<Component />)
  component.instance().method()   // we assume this runs .setState inside()
  await onNextSetState(component) // we wait for the next re-render

  // we can be sure the state is what we want it to be:
  expect(component.state('error')).toBe(false)
})

複数回待機したい場合は、Promiseの代わりにObservableのようなものを使用する方が簡単/良い場合があることに注意してください。

0
niieani