Angular服务Promise响应Jasmine测试失败,请求排查问题
问题排查:Angular Jasmine测试Promise响应失败
测试运行时出现错误:Expected 'yes' to be 'no',核心是测试Promise响应时的逻辑错误,以下是相关代码及修复方案:
相关代码
CustomerService
@Injectable({ providedIn: "root" }) export class CustomerService { constructor() {} isValid() : Promise<boolean> { return Promise.resolve(true); } }
CustomerComponent
@Component({ selector: 'app-customer', templateUrl: './customer.component.html', styleUrls: ['./customer.component.scss'] }) export class CustomerComponent implements OnInit { icon = new BehaviorSubject('yes'); constructor(private readonly customerService : CustomerService) {} ngOnInit(): void { this.customerService.isValid().then(isValid => { this.icon.next(isValid ? 'yes' : 'no'); }); } }
原测试代码(存在问题)
describe('CustomerComponent', () => { let component: CustomerComponent; let fixture: ComponentFixture<CustomerComponent>; let customerServiceSpy: jasmine.SpyObj<CustomerService>; beforeEach(async () => { customerServiceSpy = jasmine.createSpyObj<CustomerService>(['isValid']); await TestBed.configureTestingModule({ imports: [HttpClientTestingModule], declarations: [CustomerComponent], providers: [{ provide: CustomerService, useValue: customerServiceSpy }] }).compileComponents(); fixture = TestBed.createComponent(CustomerComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); fit('should change initial value of icon according to the response of isValid function', waitForAsync(() => { fixture.detectChanges(); customerServiceSpy.isValid.and.returnValue(Promise.resolve(false)); component.ngOnInit(); fixture.whenStable().then(() => { fixture.detectChanges(); expect(component.icon.value).toBe('no'); }); customerServiceSpy.isValid.and.returnValue(Promise.resolve(true)); component.ngOnInit(); fixture.whenStable().then(() => { fixture.detectChanges(); expect(component.icon.value).toBe('yes'); }); })); });
问题分析
- 异步逻辑顺序混乱:Promise是异步执行的,第一个
whenStable()的回调还未执行时,就修改了spy的返回值并再次调用ngOnInit(),两个异步操作互相覆盖。第二个ngOnInit()会先将icon设为yes,而第一个测试的断言此时才执行,必然失败。 - 单测试用例混合多场景:一个测试用例应只验证一种场景(isValid返回true或false),同时测试两种情况违反了单一职责原则,容易引发逻辑冲突。
修复方案
将测试拆分为两个独立用例,分别验证isValid返回true和false的场景,确保每个用例中spy返回值设置正确,且等待异步操作完成后再执行断言。
修复后的测试代码
describe('CustomerComponent', () => { let component: CustomerComponent; let fixture: ComponentFixture<CustomerComponent>; let customerServiceSpy: jasmine.SpyObj<CustomerService>; beforeEach(async () => { customerServiceSpy = jasmine.createSpyObj<CustomerService>(['isValid']); await TestBed.configureTestingModule({ imports: [HttpClientTestingModule], declarations: [CustomerComponent], providers: [{ provide: CustomerService, useValue: customerServiceSpy }] }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(CustomerComponent); component = fixture.componentInstance; // 不提前调用fixture.detectChanges(),手动控制ngOnInit触发时机 }); it('should create', () => { expect(component).toBeTruthy(); }); it('should set icon to "no" when isValid returns false', waitForAsync(() => { customerServiceSpy.isValid.and.returnValue(Promise.resolve(false)); component.ngOnInit(); fixture.whenStable().then(() => { fixture.detectChanges(); expect(component.icon.value).toBe('no'); }); })); it('should set icon to "yes" when isValid returns true', waitForAsync(() => { customerServiceSpy.isValid.and.returnValue(Promise.resolve(true)); component.ngOnInit(); fixture.whenStable().then(() => { fixture.detectChanges(); expect(component.icon.value).toBe('yes'); }); })); });
额外优化点
- 拆分
beforeEach:将模块配置与组件创建分离,避免重复配置模块,同时手动控制ngOnInit触发时机,提升测试灵活性。 - 使用
async/await简化异步代码(可选):让测试逻辑更易读,示例如下:
it('should set icon to "no" when isValid returns false', async () => { customerServiceSpy.isValid.and.returnValue(Promise.resolve(false)); component.ngOnInit(); await fixture.whenStable(); fixture.detectChanges(); expect(component.icon.value).toBe('no'); });
内容的提问来源于stack exchange,提问作者K.G.K.Thilina
相关产品推荐
相关产品推荐

