测试异步函数
在Jest
中测试异步函数主要有4种方法:
- 在
test
中返回的Promise
中断言 - 使用
async
和await
- 在回调函数中使用
done
函数 - 使用
resolves
和rejects
返回Promise
如果要测试的函数返回的是一个Promise
,那么我们就可以让test
返回Promise
,在Promise
中使用断言。比如:
test('the data is peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
})
})
这样,我们就可以测试这个Promise
函数了。
注意:我们一定要返回
Promise
,让Jest
知道这是一个异步函数,在Promise
的resolve
后进行断言,否则,Jest会提前断言。这个时候,Promise
的状态还没有改变,得不到我们想要的结果。如果Promise
没有resolve
,而是jest
了,那么测试会直接失败。
async
/await
在测试异步代码的时候,我们可以直接使用async
和await
。就像下面一样:
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');
}
})
在使用async
/await
测试异步代码的时候,我们要给test
函数添加async
标记,在异步代码前使用await
。
当使用async
/await
测试异步代码失败的时候,记得一定要使用assertions
来断言测试有过断言,否则测试不能通过。
使用回调函数
如果我们要测试的异步代码是在回调函数中,而不是在Promise
里,那么我们就需要使用test
函数中单参数done
来解决问题了。比如:
test('the data is peanut butter', done => {
function callback(error, data) {
if (error) {
done(error);
return;
}
try {
expect(data).toBe('peanut butter');
done();
} catch (error) {
done(error);
}
}
fetchData(callback);
})
在这个例子中,如果done()
函数从来没有执行过,那么测试会超时失败。
如果expect
执行失败,那么其将抛出一个错误,其后的done
就不会执行。如果我们想要知道具体为什么会失败,那么我们就要在测试中捕获这个错误。所以需要把expect
放入try
中,然后在catch
中捕获错误,使用done
来接收error
的值。
使用resolves
和rejects
我们也可以让test
中的函数返回一个resolves
或rejects
来测试异步代码。比如:
test('the data is peanut butter', () => {
return expect(fetchData()).resolves().toBe('peanut butter');
})
直接通过resolves
来声明我们期望Promise
的状态会变为resolve
状态,并返回peanut butter
,否则测试失败(无论Promise
是变为reject
还是resolve
后返回的不是peanut butter
)。
同理,通过rejects
,我们期望Promise
的状态会变为rejects
,并返回一个指定的错误。如:
test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
})
测试RxJs中Promise
的行为
因为RxJs
的实现机制和Promise
微任务的问题,在RxJs
中不能使用弹珠测试,那么我们就要使用Jest
中测试异步代码中的方法来解决问题了。比如:
// Some RxJS code that also consumes a Promise, so TestScheduler won't be able
// to correctly virtualize and the test will always be really asynchronous.
const myAsyncCode = () => from(Promise.resolve('something'));
it('has async code', (done) => {
myAsyncCode().subscribe((d) => {
assertEqual(d, 'something');
done();
});
});