React の Jest と Enzyme を使用して、シミュレートされたクリックが Promise を呼び出す関数を呼び出すかどうかをテストする 質問する

React の Jest と Enzyme を使用して、シミュレートされたクリックが Promise を呼び出す関数を呼び出すかどうかをテストする 質問する
  • リアクトv15.1.0
  • ジェスト v12.1.1
  • 酵素v2.3.0

クリックによって呼び出される関数内で promise を呼び出すコンポーネントをテストする方法を考えています。runAllTicks()ここでは Jest の関数が役立つと思っていましたが、promise が実行されていないようです。

成分:

import React from 'react';
import Promise from 'bluebird';

function doSomethingWithAPromise() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, 50);
  });
}

export default class AsyncTest extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      promiseText: '',
      timeoutText: ''
    };

    this.setTextWithPromise = this.setTextWithPromise.bind(this);
    this.setTextWithTimeout = this.setTextWithTimeout.bind(this);
  }

  setTextWithPromise() {
    return doSomethingWithAPromise()
      .then(() => {
        this.setState({ promiseText: 'there is text!' });
      });
  }

  setTextWithTimeout() {
    setTimeout(() => {
      this.setState({ timeoutText: 'there is text!' });
    }, 50);
  }

  render() {
    return (
      <div>
        <div id="promiseText">{this.state.promiseText}</div>
        <button id="promiseBtn" onClick={this.setTextWithPromise}>Promise</button>
        <div id="timeoutText">{this.state.timeoutText}</div>
        <button id="timeoutBtn" onClick={this.setTextWithTimeout}>Timeout</button>
      </div>
    );
  }
}

そしてテスト:

import AsyncTest from '../async';
import { shallow } from 'enzyme';
import React from 'react';

jest.unmock('../async');

describe('async-test.js', () => {
  let wrapper;

  beforeEach(() => {
    wrapper = shallow(<AsyncTest />);
  });

  // FAIL
  it('displays the promise text after click of the button', () => {
    wrapper.find('#promiseBtn').simulate('click');

    jest.runAllTicks();
    jest.runAllTimers();

    wrapper.update();

    expect(wrapper.find('#promiseText').text()).toEqual('there is text!');
  });

  // PASS
  it('displays the timeout text after click of the button', () => {
    wrapper.find('#timeoutBtn').simulate('click');

    jest.runAllTimers();

    wrapper.update();

    expect(wrapper.find('#timeoutText').text()).toEqual('there is text!');
  });
});

ベストアンサー1

更新された回答:async/を使用するとawaitコードがよりクリーンになります。以下は古いコードです。

私は以下の要素を組み合わせることでこの問題を解決しました。

  • 約束を模擬し、すぐに解決する
  • テスト関数をマークしてテストを非同期にするasync
  • クリックをシミュレートした後、次のマクロタスク約束を解決する時間を与える

あなたの例では、次のようになります。

// Mock the promise we're testing
global.doSomethingWithAPromise = () => Promise.resolve();

// Note that our test is an async function
it('displays the promise text after click of the button', async () => {
    wrapper.find('#promiseBtn').simulate('click');
    await tick();
    expect(wrapper.find('#promiseText').text()).toEqual('there is text!');
});

// Helper function returns a promise that resolves after all other promise mocks,
// even if they are chained like Promise.resolve().then(...)
// Technically: this is designed to resolve on the next macrotask
function tick() {
  return new Promise(resolve => {
    setTimeout(resolve, 0);
  })
}

この方法を使用する場合、 Enzyme のupdate()方法は十分でも必要でもありません。なぜなら、Promise は、設計上、作成された同じティック内で解決されることはないからです。ここで何が起こっているのかの詳細な説明については、以下を参照してください。この質問

元の回答:同じロジックですが、見た目は少し劣ります。NodeのsetImmediateテストを次のティックまで延期し、その時点でプロミスが解決されます。その後、Jestのdoneテストを非同期的に終了します。

global.doSomethingWithAPromise = () => Promise.resolve({});

it('displays the promise text after click of the button', (done) => {
    wrapper.find('#promiseBtn').simulate('click');

  setImmediate( () => {
    expect(wrapper.find('#promiseText').text()).toEqual('there is text!');
    done();
  })
});

これは、複数のプロミスを待機する必要がある場合に、大きなネストされたコールバックが発生するため、あまり良い方法ではありません。

おすすめ記事