如何用httpTestingController测试含多HTTP调用的findArtists方法
如何用HttpTestingController测试findArtists方法?
我的服务逻辑是:调用findArtists时,先发起一次GET请求获取艺术家ID列表,但这些ID对应的信息不完整,所以在switchMap里循环每个ID,调用getArtistDetails发起额外的HTTP请求拿完整数据,最后用forkJoin合并所有详情请求的结果。现在调用这个方法会触发1次初始请求+5次详情请求,不知道怎么用httpTestingController处理这些后续请求,测试代码一直报错。
服务代码(ServiceClass.ts)
public findArtists(searchTerm = ''): Observable<ArtistModel[]> { return this.http.get<SongsResponse>(this.resolveUrl(`/search?q=artist:"${ encodeURI(searchTerm) }"`)) .pipe( switchMap(res => { const artistObservables = res.map(artistId => this.getArtistDetails(artistId)); // 生成多个HTTP请求的Observable return forkJoin([...artistObservables]); }) ); } public getArtistDetails(artistId: number): Observable<ArtistModel> { return this.http.get<ArtistModel>(this.resolveUrl(`/artist/${ artistId }`)); }
当前测试代码
it('should search for artists', fakeAsync(() => { service.findArtists('searchTerm') .subscribe({ next: artists => { expect(artists).toBeTruthy(); expect(artists.length).toBe(5); } }); const req = httpTestingController.expectOne('api/search?q=artist:"searchTerm"'); expect(req.request.method).toBe('GET'); pending('No idea how to handle subsequent http calls.'); req.flush({ data: tracksHelper.generateTracks(5) }); }));
遇到的错误
测试报错提示:Expected one matching request for criteria "Match by function: ", found 6 requests. 这是因为总共发起了1次搜索请求+5次详情请求,而测试只处理了其中1个,剩余5个请求未被处理。
解决方案
你需要依次处理初始请求和所有后续的详情请求,确保每个HTTP请求都被捕获并返回模拟数据,最后验证没有未处理的请求。
完整测试代码示例
it('should search for artists and fetch all artist details', fakeAsync(() => { // 1. 定义模拟数据:5个艺术家ID,以及对应的详情数据 const mockArtistIds = [1, 2, 3, 4, 5]; const mockArtistDetails = mockArtistIds.map(id => ({ id, name: `Artist ${id}`, // 补充ArtistModel需要的其他字段 })); // 2. 调用服务方法并订阅结果 service.findArtists('searchTerm').subscribe({ next: artists => { expect(artists).toBeTruthy(); expect(artists.length).toBe(5); // 验证每个详情数据是否正确返回 artists.forEach((artist, index) => { expect(artist.id).toBe(mockArtistIds[index]); expect(artist.name).toBe(`Artist ${mockArtistIds[index]}`); }); } }); // 3. 处理初始搜索请求 const searchReq = httpTestingController.expectOne('api/search?q=artist:"searchTerm"'); expect(searchReq.request.method).toBe('GET'); // 注意:这里要返回艺术家ID数组,因为服务里res直接map了artistId searchReq.flush(mockArtistIds); // 4. 处理所有艺术家详情请求 mockArtistIds.forEach((id, index) => { const detailReq = httpTestingController.expectOne(`api/artist/${id}`); expect(detailReq.request.method).toBe('GET'); detailReq.flush(mockArtistDetails[index]); }); // 5. 验证所有请求都已被处理,避免残留请求导致报错 httpTestingController.verify(); }));
关键步骤说明
- 初始请求处理:确保返回的模拟数据是服务后续需要的艺术家ID数组,这样
switchMap里才能生成对应的详情请求。 - 批量处理详情请求:循环每个模拟的艺术家ID,用
expectOne捕获对应的/artist/:id请求,返回对应的详情数据。 - 验证所有请求:最后调用
httpTestingController.verify(),确保没有未被处理的HTTP请求,这是Angular HTTP测试的最佳实践。
如果你不想逐个捕获详情请求,也可以用httpTestingController.match方法批量匹配所有详情请求:
// 替代步骤4的循环 const detailReqs = httpTestingController.match(req => req.url.startsWith('api/artist/')); expect(detailReqs.length).toBe(5); detailReqs.forEach((req, index) => { req.flush(mockArtistDetails[index]); });
内容的提问来源于stack exchange,提问作者Flash
相关产品推荐
相关产品推荐

