WHAT: 测试一个使用 await 的异步函数。
使用:Angular 9, Jasmine 3.4.0。
最少的代码重现。StackBlitz
我有一个这样的函数。注意,它必须等待this.getHeaders()。每当我去掉 await,用一些同步的实现代替 getHeaders()的实现时,测试就会成功运行。
请问正确的测试方法是什么?
private async loadUserData() {
// Try to remove await - the test runs successfully
//const headers = this.getHeaders();
const headers = await this.getHeaders();
return this.httpClient
.get<UserData>(window.location.origin + '/12345', { headers })
.toPromise()
.then((data) => {
this.userData = data;
})
.catch((e) => {
console.log('Oh noooo...');
});
}
我已经尝试过了。
而不是一个二重的。
你已经很接近了。你的测试现在结构正确,但你需要一些由Angular提供的测试工具,以确保你的Promises以正确的顺序解决,你的期望是正确的。
最终,问题与Angular区域有关。因为你在你的服务中构建了承诺,而这个承诺的解析必须在一个外发请求发生之前发生。你调用 loadUserData
在你的测试中,然后在下一行你写一个断言,说 "确保这个请求发生了,如果没有,那就是一个错误"。当你在写头检索函数时,你不使用异步基元(如 Promise
或 Observable
)这个请求会 "立即 "发生。但是当你使用 Promise
,没有请求 "立即 "发生。相反,它必须先解析你的头检索函数。
幸运的是,你的测试失败只是测试环境的一个幻象功能,而不是你的实现中的一个bug。就像我说的,Angular给你提供了一些测试工具,以确保你可以在写断言之前 "刷新 "所有待定的承诺。
import { fakeAsync, tick } from '@angular/core/testing';
// instead of using `done`, which is just fine btw, we wrap our test function
// in fakeAsync(). This let's us have fine grained control over stepping through
// our code under test
it('should return user data on success', fakeAsync(() => {
const mockUserData : UserData = {
name: 'John',
surname: 'Do',
id: '12345'
};
(service as any).loadUserData().then(() => {
expect((service as any).userData).toEqual(mockUserData);
//expect(req.request.method).toEqual('GET');
});
// tick() steps through the next round of pending asynchronous activity
// this will also step through 'setTimeout' and 'setInterval' code
// you may have in your service, as well as Observable code
tick();
const req = httpTestingController.expectOne(
'https://testing-qh48mg.stackblitz.io/12345',
);
req.flush(mockUserData);
}));