在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 Service1 
 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');
 }
 })