Angular Jasmine Karma测试中如何Mock SpyOn并返回Observable
Angular 14 中用 Jasmine/Karma 测试 Observable 并模拟数据的两种方法
先梳理你现有测试代码的问题:
userModel未初始化,且getUserPrefer返回的是Observable<ApiResp<UserModel>>,直接返回userModel类型不匹配- 测试未订阅 Observable,也没有断言验证结果
- 未 mock 依赖的
CService,运行时会因找不到aConfig报错 - 测试模块应导入
HttpClientTestingModule而非HttpClientModule,避免发起真实 HTTP 请求
下面提供两种可行的解决方案:
方案一:直接 SpyOn 服务方法返回模拟 Observable
这种方式适合快速测试依赖该服务的组件,直接替换服务方法的返回值:
import { TestBed } from '@angular/core/testing'; import { UserService } from './UserService'; import { Observable, of } from 'rxjs'; import { ApiResp, UserModel } from './ApiResp'; import { CService } from './CService'; describe('UserService', () => { let service: UserService; // 模拟符合结构的响应数据 const mockUserResp: ApiResp<UserModel> = { status: 200, timestamp: new Date().toISOString(), payload: { email: 'john@test.com', id: '123' } }; beforeEach(() => { TestBed.configureTestingModule({ providers: [ UserService, // Mock CService,避免依赖真实配置 { provide: CService, useValue: { aConfig: { customer: 'https://mock-api.com/' } } } ] }); service = TestBed.inject(UserService); }); it('should get UserPreference with mocked Observable', (done) => { // SpyOn 并返回模拟的 Observable spyOn(service, 'getUserPrefer').and.returnValue(of(mockUserResp)); // 调用方法并订阅结果 service.getUserPrefer('newuserjohn').subscribe(resp => { // 断言返回数据符合预期 expect(resp.status).toBe(200); expect(resp.payload?.email).toBe('john@test.com'); // 验证方法被调用过 expect(service.getUserPrefer).toHaveBeenCalledWith('newuserjohn'); done(); // 异步测试需调用 done 通知完成 }); }); });
方案二:用 HttpClientTestingModule 模拟 HTTP 请求(更推荐)
这种方式会测试服务的真实逻辑(是否正确发起 HTTP 请求),而非直接替换方法:
import { TestBed } from '@angular/core/testing'; import { UserService } from './UserService'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { ApiResp, UserModel } from './ApiResp'; import { CService } from './CService'; describe('UserService', () => { let service: UserService; let httpMock: HttpTestingController; const mockBaseUrl = 'https://mock-api.com/'; const mockUserResp: ApiResp<UserModel> = { status: 200, timestamp: new Date().toISOString(), payload: { email: 'john@test.com', id: '123' } }; beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [ UserService, { provide: CService, useValue: { aConfig: { customer: mockBaseUrl } } } ] }); service = TestBed.inject(UserService); httpMock = TestBed.inject(HttpTestingController); }); afterEach(() => { // 验证所有 HTTP 请求都已被处理,避免内存泄漏 httpMock.verify(); }); it('should send GET request and return user preference', (done) => { const userId = 'newuserjohn'; const expectedUrl = `${mockBaseUrl}v1/getuser/${userId}`; // 调用服务方法 service.getUserPrefer(userId).subscribe(resp => { expect(resp).toEqual(mockUserResp); expect(resp.payload?.id).toBe('123'); done(); }); // 捕获并匹配 HTTP 请求 const req = httpMock.expectOne(expectedUrl); // 验证请求方法是 GET expect(req.request.method).toBe('GET'); // 返回模拟响应 req.flush(mockUserResp); }); });
内容的提问来源于stack exchange,提问作者user1015388
相关产品推荐
相关产品推荐

