初始化后修改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(); }); });
问题原因
- Jasmine SpyObj属性只读限制:通过
jasmine.createSpyObj('service', [], { val: 1 })创建SpyObj时,第三个参数定义的属性会被Jasmine设置为只读getter,后续直接赋值service.val = 2不会生效,组件始终读取初始值1。 - 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
相关产品推荐
相关产品推荐

