- リアクト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();
})
});
これは、複数のプロミスを待機する必要がある場合に、大きなネストされたコールバックが発生するため、あまり良い方法ではありません。