Angular 9中如何测试带参数的HTTP Post请求及服务错误分支
测试Angular HTTP服务的成功与错误分支指南
嘿,作为Angular新手,要测试HTTP服务确实得踩几个小坑,我来一步步帮你搞定!首先先给你提下你服务代码里的几个小问题,不然测试的时候会出问题:
subsribe拼写错了,应该是subscribe- 变量名
providerData少了个s,和类里定义的providersData不一致 - 代码里的
url变量没定义,得换成你实际的API地址 - 最关键的:你现在在服务里直接调用
subscribe,这样getProviders函数没法返回异步结果,不仅组件里不好用,测试也很难验证结果。正确的做法是让函数返回Observable,把订阅逻辑交给组件或者测试代码。
先给你修正后的服务代码:
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable, catchError, throwError, tap } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class GetProviderService{ providersData: any; constructor(private http: HttpClient) { } getProviders(param: any): Observable<any>{ const json = JSON.stringify(param); const myHeader = new HttpHeaders().set('content-type','application/json'); // 替换成你实际的API地址 const apiUrl = 'https://your-api-endpoint.com/providers'; return this.http.post<any>(apiUrl, json, {headers: myHeader}).pipe( // 用tap保存数据到providersData tap(data => { this.providersData = data; }), // 捕获错误并处理 catchError(error => { window.alert("error"); return throwError(() => error); }) ); } }
接下来是测试代码,我们用Angular自带的HttpClientTestingModule来模拟HTTP请求,不用真的调用后端API:
测试准备
首先在你的服务测试文件(比如get-provider.service.spec.ts)里导入需要的模块:
import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { GetProviderService } from './get-provider.service';
编写测试用例
describe('GetProviderService', () => { let service: GetProviderService; let httpMock: HttpTestingController; // 替换成你实际的API地址 const apiUrl = 'https://your-api-endpoint.com/providers'; beforeEach(() => { // 设置测试环境 TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [GetProviderService] }); // 注入服务和HTTP测试控制器 service = TestBed.inject(GetProviderService); httpMock = TestBed.inject(HttpTestingController); }); afterEach(() => { // 确保所有请求都被处理,避免测试泄漏 httpMock.verify(); }); // 测试成功分支 it('should fetch providers data successfully and update providersData', () => { const testParam = { category: 'electronics' }; const mockResponse = { providers: ['Tech Provider 1', 'Tech Provider 2'] }; // 调用服务方法并订阅结果 service.getProviders(testParam).subscribe(data => { // 断言返回的数据和模拟数据一致 expect(data).toEqual(mockResponse); // 断言服务里的providersData被正确更新 expect(service.providersData).toEqual(mockResponse); }); // 捕获发送的POST请求 const request = httpMock.expectOne(apiUrl); // 验证请求方法是POST expect(request.request.method).toBe('POST'); // 验证请求头正确 expect(request.request.headers.get('content-type')).toBe('application/json'); // 验证请求体和我们传入的参数一致 expect(request.request.body).toEqual(JSON.stringify(testParam)); // 模拟后端返回成功响应 request.flush(mockResponse); }); // 测试错误分支 it('should handle error when fetching providers and show alert', () => { const testParam = { category: 'invalid' }; const mockError = new Error('API connection failed'); // 模拟window.alert,避免测试时弹出真实的弹窗 spyOn(window, 'alert'); // 调用服务方法并订阅错误分支 service.getProviders(testParam).subscribe({ next: () => fail('这个测试应该触发错误,不应该进入next分支'), error: (error) => { // 断言错误信息正确 expect(error).toEqual(mockError); // 断言alert被调用,且内容是"error" expect(window.alert).toHaveBeenCalledWith('error'); } }); // 捕获发送的POST请求 const request = httpMock.expectOne(apiUrl); // 模拟后端返回错误响应 request.error(new ErrorEvent('network error', { error: mockError })); }); });
关键测试点解释
HttpTestingController:用来捕获和模拟HTTP请求,让我们不用依赖真实后端就能测试服务逻辑expectOne(apiUrl):确保只有一个针对该URL的请求被发送,避免测试混乱flush():模拟后端返回成功数据error():模拟后端返回错误spyOn(window, 'alert'):替换真实的alert函数,避免测试时弹出弹窗,同时可以验证它是否被正确调用
这样就能完整覆盖getProviders函数的成功和错误分支啦!
内容的提问来源于stack exchange,提问作者DHRUVIN MASHRUWALA
相关产品推荐
相关产品推荐

