异步示例
首先,按照 入门 指南中的记录,在 Jest 中启用 Babel 支持。
¥First, enable Babel support in Jest as documented in the Getting Started guide.
让我们实现一个从 API 获取用户数据并返回用户名的模块。
¥Let's implement a module that fetches user data from an API and returns the user name.
import request from './request';
export function getUserName(userID) {
return request(`/users/${userID}`).then(user => user.name);
}
在上面的实现中,我们期望 request.js
模块返回一个 promise。我们链接调用 then
以接收用户名。
¥In the above implementation, we expect the request.js
module to return a promise. We chain a call to then
to receive the user name.
现在想象一下 request.js
的实现,它进入网络并获取一些用户数据:
¥Now imagine an implementation of request.js
that goes to the network and fetches some user data:
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__
不起作用)。它可能看起来像这样:
¥Because we don't want to go to the network in our test, we are going to create a manual mock for our request.js
module in the __mocks__
folder (the folder is case-sensitive, __MOCKS__
will not work). It could look something like this:
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.`,
}),
);
});
}
现在让我们为异步功能编写一个测试。
¥Now let's write a test for our async functionality.
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。
¥We call jest.mock('../request')
to tell Jest to use our manual mock. it
expects the return value to be a Promise that is going to be resolved. You can chain as many Promises as you like and call expect
at any time, as long as you return a Promise at the end.
.resolves
有一种不太详细的方法,使用 resolves
与任何其他匹配器一起解开已履行的 promise 的值。如果 promise 被拒绝,则断言将失败。
¥There is a less verbose way using resolves
to unwrap the value of a fulfilled promise together with any other matcher. If the promise is rejected, the assertion will fail.
it('works with resolves', () => {
expect.assertions(1);
return expect(user.getUserName(5)).resolves.toBe('Paul');
});
async
/await
也可以使用 async
/await
语法编写测试。以下是你如何编写之前的相同示例:
¥Writing tests using the async
/await
syntax is also possible. Here is how you'd write the same examples from before:
// 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
文件中启用该功能。
¥To enable async/await in your project, install @babel/preset-env
and enable the feature in your babel.config.js
file.
错误处理
¥Error handling
可以使用 .catch
方法来处理错误。确保添加 expect.assertions
以验证是否调用了一定数量的断言。否则,已履行的 promise 不会通过测试:
¥Errors can be handled using the .catch
method. Make sure to add expect.assertions
to verify that a certain number of assertions are called. Otherwise a fulfilled promise would not fail the test:
// 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
辅助程序类似。如果 promise 兑现,测试将自动失败。expect.assertions(number)
不是必需的,但建议验证测试期间是否调用了一定数量的 assertions。否则很容易忘记 return
/await
.resolves
断言。
¥The.rejects
helper works like the .resolves
helper. If the promise is fulfilled, the test will automatically fail. expect.assertions(number)
is not required but recommended to verify that a certain number of assertions are called during a test. It is otherwise easy to forget to return
/await
the .resolves
assertions.
// 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 处获取。
¥The code for this example is available at examples/async.
如果你想测试计时器(例如 setTimeout
),请查看 计时器模拟 文档。
¥If you'd like to test timers, like setTimeout
, take a look at the Timer mocks documentation.