Reactアプリケーションの統合テスト、つまり、多くのコンポーネントを一緒にテストするテストを書いています。外部サービスへの呼び出しをモックしたいです。
問題は、非同期コールバックが実行される前にテストが実行され、テストが失敗するように見えることです。
とにかくこれの周りにありますか?非同期コードの呼び出しが完了するのをどうにかして待つことはできますか?
ここにいくつかの悪い点があります擬似私のポイントを説明するコード。
親をマウントすると、その子コンポーネントが外部サービスから返されたコンテンツをレンダリングすることをテストしたいと思います。
class Parent extends component
{
render ()
{
<div>
<Child />
</div>
}
}
class Child extends component
{
DoStuff()
{
aThingThatReturnsAPromise().then((result) => {
Store.Result = result
})
}
render()
{
DoStuff()
return(<div>{Store.Result}</div>)
}
}
function aThingThatReturnsAPromise()
{
return new Promise(resolve =>{
eternalService.doSomething(function callback(result) {
resolve(result)
}
}
}
テストでこれを行うと、コールバックが発生する前に実行されるため失敗します。
jest.mock('eternalService', () => {
return jest.fn(() => {
return { doSomething: jest.fn((cb) => cb('fakeReturnValue');
});
});
describe('When rendering Parent', () => {
var parent;
beforeAll(() => {
parent = mount(<Parent />)
});
it('should display Child with response of the service', () => {
expect(parent.html()).toMatch('fakeReturnValue')
});
});
これをテストするにはどうすればよいですか? angular zonejsでこれを解決しますが、Reactに同等のアプローチはありますか?
以下は、保留中のPromiseが解決されるまで待機するスニペットです。
const flushPromises = () => new Promise(setImmediate);
setImmediate は非標準の機能であることに注意してください(標準になることは期待されていません)。しかし、テスト環境に十分であれば、それは良い解決策です。その説明:
このメソッドは、ブラウザがイベントや表示の更新などの他の操作を完了した直後に、長時間実行されている操作を分割し、コールバック関数を実行するために使用されます。
Async/awaitを使用して大まかに使用する方法は次のとおりです。
it('is an example using flushPromises', async () => {
const wrapper = mount(<App/>);
await flushPromises();
wrapper.update(); // In my experience, Enzyme didn't always facilitate component updates based on state changes resulting from Promises -- hence this forced re-render
// make assertions
});
私はこれをよく使いました このプロジェクトでは 実際の実例を使用したい場合。
Reactがあなたが探しているものを成し遂げるために生まれたものは何も知らない。
ただし、セットアップの完了後にbeforeAll()の@doneを呼び出すことで、同様のコードでこれを実現できました。以下のコードの変更をご覧ください。
let setupComplete;
jest.mock('eternalService', () => {
return jest.fn(() => {
return { doSomething: jest.fn((cb) => { cb('fakeReturnValue'); setupComplete(); }) };
});
.
.
.
beforeAll(done => {
parent = mount(<Parent />)
setupComplete = done;
});
});
私はそれらを使用したことはありませんが、潜在的な関心はJestの runAllTicks および runAllImmediates です。
JavaScriptの非同期の性質は、JavaScriptの単体テストが成熟するのに長い時間がかかった主な理由です。 Jestは、ここで説明する非同期コードをテストするための多くのパターンを提供します。 http://jestjs.io/docs/en/asynchronous.html async/awaitを最も活用しています非常に読みやすいテストを作成するために広範囲に。
ただし、非同期メソッドをモックしているので、非同期コードは必要ありません。下記参照。
let parent
let eternalService = jest.fn(() => 'fakeReturnValue');
beforeEach(() => {
parent = mount(<Parent />)
parent.instance().externalService = externalService
})
describe('When rendering Parent', () => {
it('should display Child with response of the service', () => {
expect(parent.html()).toMatch('fakeReturnValue')
});
});
モジュールまたはファイルからaThingThatReturnsAPromise()
をエクスポートしてから、テストケースにインポートすることをお勧めします。
aThingThatReturnsAPromise()
はpromiseを返すため、Jestの非同期テスト機能を利用できます。 Jestはあなたの約束が解決するのを待ってから、あなたはあなたの主張をすることができます。
describe('When rendering Parent', () => {
var parent;
beforeAll(() => {
parent = mount(<Parent />)
});
it('should display Child with response of the service', () => {
expect.assertions(1);
return aThingThatReturnsAPromise().then( () => {
expect(parent.html()).toMatch('fakeReturnValue');
});
});
});
詳細については、JestがJest DocsのPromisesでテストケースを処理する方法をお読みください here
擬似コードは、リファクタリングしてReactライフサイクル(componentWillMount()
componentDidMount()
を使用すると、テストがはるかに簡単になります。ただし、以下は、テストコードの変更に対するテストされていない擬似コードは、お気軽にテストして更新してください。
describe('When rendering Parent', () => {
it('should display Child with the response of the service', function(done) => {
const parent = mount(<Parent />);
expect(parent.html()).toMatch('fakeReturnValue');
done();
});
});