跳至主要內容
版本:29.7

非同步範例

首先,按照 入門 指南中的說明,在 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));
});
});
}

由於我們不希望在測試中連線到網路,我們將在 __mocks__ 資料夾中為我們的 request.js 模組建立一個手動模擬(資料夾名稱區分大小寫,__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.slice('/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).toBe('Mark'));
});

我們呼叫 jest.mock('../request') 來告訴 Jest 使用我們的手動模擬。it 預期傳回值是一個 Promise,且會被解析。你可以串聯任意數量的 Promise,並隨時呼叫 expect,只要最後傳回一個 Promise 即可。

.resolves

使用 resolves 有一種較不囉嗦的方式,可以將已完成承諾的值與任何其他匹配器一起解開。如果承諾被拒絕,則斷言將會失敗。

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

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).toBe('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.toBe('Paul');
});

若要啟用專案中的 async/await,請安裝 @babel/preset-env,並在 babel.config.js 檔案中啟用此功能。

錯誤處理

可以使用 .catch 方法來處理錯誤。請務必加入 expect.assertions 來驗證已呼叫特定數量的斷言。否則已完成的承諾不會使測試失敗

// Testing for async errors using Promise.catch.
it('tests error with promises', () => {
expect.assertions(1);
return user.getUserName(2).catch(error =>
expect(error).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 (error) {
expect(error).toEqual({
error: 'User with 1 not found.',
});
}
});

.rejects

.rejects 輔助程式的工作原理與 .resolves 輔助程式類似。如果承諾已完成,則測試將自動失敗。expect.assertions(number) 不是必需的,但建議用來驗證在測試期間已呼叫特定數量的 斷言。否則很容易忘記 return/await .resolves 斷言。

// 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.',
});
});

此範例的程式碼可在 examples/async 取得。

如果您想測試計時器,例如 setTimeout,請查看 計時器模擬 文件。