Angular嵌套REST Get调用单元测试求助:getSingle重复监视问题
解决Angular子类Service两次调用super.getSingle的单元测试问题
你碰到的“getSingle already spied upon”错误,是因为重复对父类的getSingle方法设置spy导致的。下面是针对你这个getSingle方法的完整单元测试方案,覆盖所有分支场景:
测试准备
假设你的父类Service叫BaseService,子类叫ChildService,测试文件初始化代码如下:
import { TestBed } from '@angular/core/testing'; import { ChildService } from './child.service'; import { BaseService } from './base.service'; import { of, throwError } from 'rxjs'; describe('ChildService', () => { let service: ChildService; let baseServiceSpy: jasmine.SpyObj<BaseService>; beforeEach(() => { // 创建父类Service的spy对象 const spy = jasmine.createSpyObj('BaseService', ['getSingle']); TestBed.configureTestingModule({ providers: [ ChildService, { provide: BaseService, useValue: spy } ] }); service = TestBed.inject(ChildService); baseServiceSpy = TestBed.inject(BaseService) as jasmine.SpyObj<BaseService>; // 每次测试前重置spy调用记录,避免测试间干扰 baseServiceSpy.getSingle.calls.reset(); });
测试场景1:第一次调用返回带cId的数据,第二次调用成功返回用户信息
it('should call super.getSingle twice and merge user data when cId exists', (done) => { // 模拟第一次调用的返回值 const mockFirstResponse = { test: { cId: 'user-123', otherProp: 'test-content' } }; // 模拟第二次调用的返回值 const mockSecondResponse = { user: { name: 'Abhinav', id: 'user-123', uid: 'uid-456' } }; // 让spy按顺序返回两个Observable,对应两次super.getSingle调用 baseServiceSpy.getSingle.and.returnValues( of(mockFirstResponse), of(mockSecondResponse) ); service.getSingle('target-id', 'any-unit').subscribe((result) => { // 验证调用次数和参数 expect(baseServiceSpy.getSingle).toHaveBeenCalledTimes(2); expect(baseServiceSpy.getSingle).toHaveBeenCalledWith('target-id', 'test'); expect(baseServiceSpy.getSingle).toHaveBeenCalledWith('user-123', 'testing'); // 验证返回结果是否正确合并用户数据 expect(result.test.user.name).toBe('Abhinav'); expect(result.test.user.uid).toBe('uid-456'); expect(result.test.otherProp).toBe('test-content'); done(); }); });
测试场景2:第一次调用返回无cId的数据,仅调用一次super.getSingle
it('should call super.getSingle once when no cId exists in response', (done) => { const mockResponse = { test: { otherProp: 'test-content' // 不含cId字段 } }; baseServiceSpy.getSingle.and.returnValue(of(mockResponse)); service.getSingle('target-id', 'any-unit').subscribe((result) => { expect(baseServiceSpy.getSingle).toHaveBeenCalledTimes(1); expect(baseServiceSpy.getSingle).toHaveBeenCalledWith('target-id', 'test'); expect(result.test.user).toBeUndefined(); done(); }); });
测试场景3:第一次调用成功有cId,但第二次调用失败
it('should return original data when second super.getSingle call fails', (done) => { const mockFirstResponse = { test: { cId: 'user-123', otherProp: 'test-content' } }; baseServiceSpy.getSingle.and.returnValues( of(mockFirstResponse), throwError(() => new Error('Fetch user failed')) ); service.getSingle('target-id', 'any-unit').subscribe((result) => { expect(baseServiceSpy.getSingle).toHaveBeenCalledTimes(2); expect(result.test.user).toBeUndefined(); expect(result.test.otherProp).toBe('test-content'); done(); }); });
测试场景4:第一次调用super.getSingle出错
it('should emit error when first super.getSingle call fails', (done) => { const mockError = new Error('Fetch base data failed'); baseServiceSpy.getSingle.and.returnValue(throwError(() => mockError)); service.getSingle('target-id', 'any-unit').subscribe({ next: () => fail('Should not emit next value'), error: (err) => { expect(err).toBe(mockError); expect(baseServiceSpy.getSingle).toHaveBeenCalledTimes(1); done(); } }); }); });
关键注意点
- 使用
returnValues按顺序返回不同Observable,一次spy即可处理多次调用,避免重复spy报错。 - 每次测试前用
calls.reset()清空spy的调用记录,保证测试独立性。 - 用
done()处理异步Observable的测试,确保断言在流完成后执行。 - 覆盖所有代码分支,保证测试完整性。
内容的提问来源于stack exchange,提问作者Abhinav Parashar
相关产品推荐
相关产品推荐

