使用 Jasmine 和 await 测试基于 HTTP 的异步函数:预期有一个匹配条件“...”的请求,找到 none
Testing async HTTP-based function with Jasmine and await: Expected one matching request for criteria "...", found none
内容:测试使用 await
的异步函数
搭配:Angular9,茉莉花 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...');
});
}
我尝试了什么:
- 可能是StackBitz中的url不正确,但是
在本地测试时我确定是这样,所以这不应该是根
原因
- 不确定,fakeAsync() 是否有帮助 - "pure" http 测试有效...
不是被骗的:
- Not sure if timeout on flush would help in my case
- I don't use any params on my url
你非常接近。您的测试现在结构正确,但您需要 Angular 提供的一些测试实用程序来确保您的 Promise 以正确的顺序解析,以使您的期望正确。
最终问题与 Angular 区域有关。因为您在您的服务中构建承诺,并且承诺解决必须在传出请求发生之前发生。您在测试中调用 loadUserData
,然后在下一行中编写一个断言 "make sure that this request happened, if not that's an error"。当您以不使用异步原语(如 Promise
或 Observable
)的方式编写 header 检索函数时,会发生此请求 "immediately"。但是当你使用 Promise
时,没有请求发生 "immediately"。相反,它必须先解析您的 header 检索函数。
幸运的是,您的测试失败只是测试环境的幻象功能,而不是您实现中的错误。正如我所说,Angular 为您提供了一些测试工具,以确保您可以在编写断言之前 "flush" 所有未决的承诺。
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);
}));
已更新Stackblitz. Docs on fakeAsync
. Related question on testing Http code in Angular using tick
.
内容:测试使用 await
的异步函数搭配:Angular9,茉莉花 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...');
});
}
我尝试了什么:
- 可能是StackBitz中的url不正确,但是 在本地测试时我确定是这样,所以这不应该是根 原因
- 不确定,fakeAsync() 是否有帮助 - "pure" http 测试有效...
不是被骗的:
- Not sure if timeout on flush would help in my case
- I don't use any params on my url
你非常接近。您的测试现在结构正确,但您需要 Angular 提供的一些测试实用程序来确保您的 Promise 以正确的顺序解析,以使您的期望正确。
最终问题与 Angular 区域有关。因为您在您的服务中构建承诺,并且承诺解决必须在传出请求发生之前发生。您在测试中调用 loadUserData
,然后在下一行中编写一个断言 "make sure that this request happened, if not that's an error"。当您以不使用异步原语(如 Promise
或 Observable
)的方式编写 header 检索函数时,会发生此请求 "immediately"。但是当你使用 Promise
时,没有请求发生 "immediately"。相反,它必须先解析您的 header 检索函数。
幸运的是,您的测试失败只是测试环境的幻象功能,而不是您实现中的错误。正如我所说,Angular 为您提供了一些测试工具,以确保您可以在编写断言之前 "flush" 所有未决的承诺。
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);
}));
已更新Stackblitz. Docs on fakeAsync
. Related question on testing Http code in Angular using tick
.