代码异步运行在 JavaScript 里很常见。当你有异步代码运行时,Jest 需要知道它正在测试的代码何时完成,然后才能运行另外一个测试。Jest 有几种方法处理这种情况。
Callbacks
常见的异步模式就是回调。
例如,你有一个 fetchData(callback)
函数拉取一些数据并在它完成的时候调用 callback(data)
。你想要测试这个返回的数据是字符串 peanut butter
。
默认情况下,Jest测试在执行结束时就完成了(调用是异步的,但此时测试已经结束)。这意味着该测试将无法正常工作:
// Don't do this!
test('the data is peanut butter', () => {
function callback(data) {
expect(data).toBe('peanut butter');
}
fetchData(callback);
});
问题是,一旦 fetchData
完成,测试就会在调用回调之前完成。
有一种替代形式的 test
可以解决这个问题。不要将测试放在带有空参数的函数中,而是使用一个名为done
的参数。Jest将等到调用done
回调函数后才完成测试。(调用 done 回调函数的时候已经发生了断言)
test('the data is peanut butter', done => {
function callback(data) {
expect(data).toBe('peanut butter');
done();
}
fetchData(callback);
});
如果 done()
没有被调用,这个测试将失败,这也是你所期望看到的。
Promises
如果你的代码返回 promises,这里有一个简单的方式处理异步测试。Jest 从测试中返回一个 promise,并且 Jest 将等待 promise 被 resolve。如果这个 promise 被 rejected,这个测试将自动失败。
例如,我们看看 fetchData
,代替使用回调,而是返回一个 promise,该 promise 被 resolve 为字符串 ’peanut butter‘
。我们如下测试:
test('the data is peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});
确保返回 promise - 如果你省略了 return 语句,你的测试将在 fetchData
resolves 之前完成,然后 then()
才有机会执行回调。
如果你希望一个 promise 被 rejected,可以使用 .catch
方法。确保添加 expect.assertions
来验证调用了一定数量的断言。否则,一个 fulfilled promise 将不会失败。(因为承诺执行成功,所以不可能走下面的断言语句,测试也不会报错;所以我们需要期望断言了一次,如果没有发生一次断言,则测试失败;如果发生了断言,断言是否成功决定测试成败)
test('the fetch fails with an error', () => {
expect.assertions(1);
return fetchData().catch(e => expect(e).toMatch('error'));
});
.resolves / .rejects
你也可以在你的 expect 语句中使用 .resolves
匹配器,Jest 将等待该 promise resolve。如果这个 promise rejected,这个测试将自动失败。
test('the data is peanut butter', () => {
return expect(fetchData()).resolves.toBe('peanut butter');
});
确保返回断言 - 如果你省略 return
语句,那么测试将在解析fetchData返回的承诺之前完成,then()
才有机会执行回调。
如果你期望一个 promise 被 rejected,则使用 .rejects
匹配器。工作原理和 .resolves
类似。如果 promise 是 fulfilled,这个测试将自动失败。
test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
});
Async / Await
或者,你可以在你的测试中使用 async
和 await
。只需在传递给测试的函数前面使用async关键字。例如,相同的fetchData
场景可以通过以下方式进行测试:
test('the data is peanut butter', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter');
});
test('the fetch fails with an error', async () => {
expect.assertions(1);
try {
await fetchData();
} catch (e) {
expect(e).toMatch('error');
}
});
当然。你可以用 .resolves
或 .rejects
组合 async 和 await 。
test('the data is peanut butter', async () => {
await expect(fetchData()).resolves.toBe('peanut butter');
});
test('the fetch fails with an error', async () => {
await expect(fetchData()).rejects.toThrow('error');
});
在这些案例中,async
和 await
都非常有效,就像语法糖一样,实现了与 promises 例子相同的逻辑。
网友评论