测试Observable管道tap内httpCache.put调用失败,求解决方案
测试
httpCache.put调用失败的原因分析与解决办法 业务代码
public getTransportAgreement(): Observable<TransportAgreement> { const cache: TransportAgreement = this.httpCache.get( this.agreementUrl ) as unknown as TransportAgreement; if (cache) { return of(cache); } return this.http.get(this.agreementUrl).pipe( tap((agreement: TransportAgreement) => { this.httpCache.put(this.agreementUrl, agreement as any); }) ); }
原测试代码
fit('can get put transport agreement cache', () => { spectatorHttp.service.getTransportAgreement().subscribe(() => { expect(spectatorHttpCache.service.put).toHaveBeenCalled(); }); });
失败原因
- 缓存命中跳过put逻辑:如果
httpCache.get默认返回非空值,方法会直接返回缓存的Observable,完全不会进入HTTP请求分支,tap里的put操作根本不会执行,断言自然失败。 - HTTP请求未完成:测试仅订阅了目标方法,但没有mock HTTP请求的响应结果,Observable处于未完成状态,
tap内的逻辑不会触发,且测试可能在异步操作结束前就完成了断言。
解决办法
步骤1:强制走HTTP请求分支
在测试开始前mockhttpCache.get返回null,确保代码不会命中缓存:
spyOn(spectatorHttpCache.service, 'get').and.returnValue(null);
步骤2:Mock并触发HTTP响应完成
用Spectator的HTTP测试工具,手动触发请求的响应,让Observable完成,确保tap逻辑执行:
const mockAgreement: TransportAgreement = { /* 填充测试用的协议数据 */ }; spectatorHttp.expectOne(spectatorHttp.service['agreementUrl']).flush(mockAgreement);
步骤3:避免异步测试提前结束
确保测试在异步操作完成后再执行断言,可直接利用Spectator的HTTP工具特性,无需额外的done回调。
修改后的完整测试代码
fit('should call httpCache.put when fetching transport agreement', () => { // 1. Mock缓存为空,强制走HTTP请求分支 spyOn(spectatorHttpCache.service, 'get').and.returnValue(null); // 2. 订阅目标方法 spectatorHttp.service.getTransportAgreement().subscribe(); // 3. Mock并触发HTTP响应,触发tap内的put逻辑 const mockAgreement: TransportAgreement = { id: 'test-id', /* 其他必要字段 */ }; spectatorHttp.expectOne(spectatorHttp.service['agreementUrl']).flush(mockAgreement); // 4. 精准断言put的调用参数 expect(spectatorHttpCache.service.put).toHaveBeenCalledWith( spectatorHttp.service['agreementUrl'], mockAgreement ); });
内容的提问来源于stack exchange,提问作者Tobias Foxtrot
相关产品推荐
相关产品推荐

