在Angular单元测试中,对于需要Mock的Service,我们可以通过spy
来模拟返回结果。
今天遇到一个问题比较尴尬,花了好长时间。在Angular方法里,通过嵌套的HttpClient请求处理数据。但是在进行单元测试的时候,发现如果单纯的模拟post
方法,发现,始终只返回最后一次设置的结果。
最终发现,可以通过and.returnValues来返回多个结果。
通过将 spy 与 and.returnValues 链接起来,对该函数的所有调用将按顺序返回特定值,直到它到达返回值列表的末尾,此时它将为所有后续调用返回 undefined。
官方示例代码
1 | describe("A spy, when configured to fake a series of return values", function() { |
实践模拟HttpClient多次请求代码
- Angular Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25getStudents(students: any): Observable<any> {
return this.postRequest(`api/students`, students).pipe(
mergeMap(result => {
return new Observable(subscriber =>{
if (result) {
result.map(student => {
if(student.family === 'ABC'){
const attributes = students.find(x => x.Id == student.Id).attributes;
this.postRequest(`api/students/GetBucketIdsByAttributes`, attributes).subscribe(bucketIds =>{
student.finalBucketIds = bucketIds;
subscriber.next([student]);
})
}
else {
subscriber.next([unit]);
}
});
}
else {
subscriber.next([]);
};
});
})
);
} - 在上面的代码中,我们需要模拟两次
postRequest
的输出结果 - 测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27describe('StudentsService', () => {
let httpMockMethods = jasmine.createSpyObj(['get', 'post', 'put', 'delete']);
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{ provide: HttpClient, useValue: httpMockMethods }
],
imports: [
HttpClientTestingModule
]
});
httpMock = TestBed.get(HttpTestingController);
});
afterEach(() => {
httpMock.verify();
httpMockMethods = jasmine.createSpyObj(['get', 'post', 'put', 'delete']);
});
it('Should Run HttpClient Request Twice', async () => {
httpMockMethods.post.and.returnValues(of([{ unitId: '123', family: Families.ymc2, bucketIds: JSON.stringify(valueBucketIds) }]), of(bucketId));
projectTreeService.getBucketIds(request).subscribe({
next: result => {
expect(result[0].finalBucketIds).toEqual(bucketId);
}
});
expect(httpMockMethods.post.calls.count()).toBe(2, 'two call');
}
})