You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 20:20:17