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

初始化后修改Jasmine Spy对象值失败问题排查

Angular测试中服务值更新后组件未同步的问题分析

组件与服务代码

@Component({
  selector: 'app-root',
  template: '',
})
export class AppComponent implements OnInit {
  constructor(private service: Service) {}
  ngOnInit(): void {
    console.log('Init with', this.service.val);
  }
  getValue() {
    return this.service.val;
  }
}

@Injectable({
  providedIn: 'root',
})
export class Service {
  val: number = 0;
}

测试代码

describe('AppComponent', () => {
  let comp: AppComponent;
  let service: jasmine.SpyObj<Service>;
  beforeEach(() => {
    service = jasmine.createSpyObj('service', [], { val: 1 });
    TestBed.configureTestingModule({
      declarations: [AppComponent],
      providers: [{ provide: Service, useValue: service }],
    });

    const fixture = TestBed.createComponent(AppComponent);
    comp = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('service value is 1', () => {
    expect(comp.getValue()).toBe(1);
  });

  it('override service value to 2', () => {
    service.val = 2;
    TestBed.inject(Service).val = 2;
    expect(comp.getValue()).toBe(2); //  failed: Expected 1 to be 2.
    comp.ngOnInit();
   
  });
});

问题原因

  1. Jasmine SpyObj属性只读限制:通过jasmine.createSpyObj('service', [], { val: 1 })创建SpyObj时,第三个参数定义的属性会被Jasmine设置为只读getter,后续直接赋值service.val = 2不会生效,组件始终读取初始值1。
  2. ngOnInit多次执行的原因:每个测试用例会独立执行beforeEach逻辑,其中fixture.detectChanges()会触发组件的ngOnInit;第二个测试用例中又手动调用了comp.ngOnInit(),所以总共执行三次,且每次读取的都是未修改的服务值1。

解决方案

方法1:修改SpyObj的创建方式

放弃用第三个参数初始化属性,改为创建SpyObj后手动赋值,让属性变为可写:

beforeEach(() => {
  // 只指定需要spy的方法(无方法则传空数组),之后手动赋值属性
  service = jasmine.createSpyObj('Service', []);
  service.val = 1; // 手动初始化值
  TestBed.configureTestingModule({
    declarations: [AppComponent],
    providers: [{ provide: Service, useValue: service }],
  });

  const fixture = TestBed.createComponent(AppComponent);
  comp = fixture.componentInstance;
  fixture.detectChanges();
});

方法2:用普通对象模拟服务(更简单)

如果不需要对服务方法做spy,直接用普通对象模拟即可:

beforeEach(() => {
  service = { val: 1 } as jasmine.SpyObj<Service>; // 类型断言兼容SpyObj类型
  TestBed.configureTestingModule({
    declarations: [AppComponent],
    providers: [{ provide: Service, useValue: service }],
  });

  const fixture = TestBed.createComponent(AppComponent);
  comp = fixture.componentInstance;
  fixture.detectChanges();
});

调整后的测试用例

删除多余的TestBed.inject操作,直接修改service变量即可:

it('override service value to 2', () => {
  service.val = 2;
  expect(comp.getValue()).toBe(2); // 现在会通过
  comp.ngOnInit(); // 会打印"Init with 2"
});

内容的提问来源于stack exchange,提问作者cpper

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 13:21:21