メインコンテンツへスキップ
Version: Next

非同期の事例

まず、Getting Started ガイドに纏められているように Jest で Babel のサポートを有効にします。

APIからユーザのデータを取得してユーザ名を返すモジュールを実装してみましょう。

user.js
import request from './request';
export function getUserName(userID) {
return request('/users/' + userID).then(user => user.name);
}

上記の実装では、request.jsモジュールがpromiseを返却します。 それにチェーンしてthenを呼び出すことで、ユーザー名を受け取れます。

ここでネットワーク経由でユーザのデータを取得する request.js の実装を想像してみましょう。

request.js
const http = require('http');
export default function request(url) {
return new Promise(resolve => {
// This is an example of an http request, for example to fetch
// user data from an API.
// This module is being mocked in __mocks__/request.js
http.get({path: url}, response => {
let data = '';
response.on('data', _data => (data += _data));
response.on('end', () => resolve(data));
});
});
}

テストではネットワークへの接続は行いたくないので、 request.jsモジュールのマニュアルモックを__mocks__フォルダに作成することにします(このフォルダ名は小文字の必要があり、__MOCKS__ では動作しません)。 こんな感じのものでしょうか:

__mocks__/request.js
const users = {
4: {name: 'Mark'},
5: {name: 'Paul'},
};
export default function request(url) {
return new Promise((resolve, reject) => {
const userID = parseInt(url.substr('/users/'.length), 10);
process.nextTick(() =>
users[userID]
? resolve(users[userID])
: reject({
error: 'User with ' + userID + ' not found.',
}),
);
});
}

次に非同期機能のテストを書いてみましょう。

__tests__/user-test.js
jest.mock('../request');
import * as user from '../user';
// The assertion for a promise must be returned.
it('works with promises', () => {
expect.assertions(1);
return user.getUserName(4).then(data => expect(data).toEqual('Mark'));
});

マニュアルモックを使用するようにjest.mock('../request')を呼び出します。 itは戻り値が解決されようとしているPromiseであることを期待します。 最後にPromiseを返す限り、任意の数だけPromiseをチェーンして任意のタイミングでexpect を呼び出すことができます。

.resolves#

resolvesを使用することで、他のいかなるマッチャと共に成功したpromiseの値を取り出すための記述量を減らすことができます。 promiseがrejectされた場合はアサーションは失敗します。

it('works with resolves', () => {
expect.assertions(1);
return expect(user.getUserName(5)).resolves.toEqual('Paul');
});

async/await#

async/awaitシンタックスを使用したテストも可能です。 以下に前述した例と同じものをasync/awaitでどのように書くかを示します:

// async/await can be used.
it('works with async/await', async () => {
expect.assertions(1);
const data = await user.getUserName(4);
expect(data).toEqual('Mark');
});
// async/await can also be used with `.resolves`.
it('works with async/await and resolves', async () => {
expect.assertions(1);
await expect(user.getUserName(5)).resolves.toEqual('Paul');
});

プロジェクトでasync/awaitを有効にするには、@babel-preset-envをインストールし、 babel.config.jsファイルで機能を有効化します。

エラー処理#

エラーは .catch メソッドで処理することができます。 想定した数のアサーションが呼ばれたことを確認するため、expect.assertionsを必ず追加して下さい。 さもなければpromiseがrejectされなかった場合にテストが失敗したと判定されません:

// Testing for async errors using Promise.catch.
it('tests error with promises', () => {
expect.assertions(1);
return user.getUserName(2).catch(e =>
expect(e).toEqual({
error: 'User with 2 not found.',
}),
);
});
// Or using async/await.
it('tests error with async/await', async () => {
expect.assertions(1);
try {
await user.getUserName(1);
} catch (e) {
expect(e).toEqual({
error: 'User with 1 not found.',
});
}
});

.rejects#

この.rejects ヘルパーは.resolves ヘルパーと同様に動作します。 promiseが成功した場合は、テストは自動的に失敗します。 expect.assertions(number) は必須ではありませんが、テスト中に特定の数のassertionsが呼び出されることを確認することをお勧めします。 これを利用する理由としては、 .resolves の結果をreturn/await することを忘れてしまうことが良くあるからです。

// Testing for async errors using `.rejects`.
it('tests error with rejects', () => {
expect.assertions(1);
return expect(user.getUserName(3)).rejects.toEqual({
error: 'User with 3 not found.',
});
});
// Or using async/await with `.rejects`.
it('tests error with async/await and rejects', async () => {
expect.assertions(1);
await expect(user.getUserName(3)).rejects.toEqual({
error: 'User with 3 not found.',
});
});

The code for this example is available at examples/async.

setTimeout のようなタイマーをテストしたい場合は、Timer mocks ドキュメントを読んで下さい。