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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 05:00:39