Angular单元测试:创建未决议Promise并验证决议后执行逻辑
验证Promise决议后执行方法的测试方案
问题背景
我有一个返回Promise的函数waitUntilIsReady:
(...) private inProgressSubject = new BehaviorSubject<boolean>(true) waitUntilIsReady(): Promise<void> { const untilInProgress = new Subject<void>() return new Promise((resolve) => { this.inProgress.pipe(takeUntil(untilInProgress)).subscribe((inProgress) => { if (inProgress) { return } untilInProgress.next() untilInProgress.complete() resolve() }) }) } (...)
在组件的ngOnInit中,通过该函数确保特定逻辑仅在就绪后执行:
ngOnInit(): void { this.service.waitUntilUserIsReady().then(() => { this.otherService.doSomething() }) }
核心疑问
- 如何编写测试验证
doSomething()仅在Promise决议后执行,而非之前? - 如何在测试中手动控制Promise的决议时机(类似通过Observable Subject调用
next()的方式)? - 曾尝试
Observable.of().pipe(delay(500)).toPromise(),但toPromise已废弃,需要替代方案。
解决方案
1. 模拟服务方法,创建可手动控制的Promise
通过保存Promise的resolve函数引用,在测试中按需触发决议:
describe('YourComponent', () => { let component: YourComponent; let fixture: ComponentFixture<YourComponent>; let resolveWaitUntilReady: () => void; const mockService = jasmine.createSpyObj('YourService', ['waitUntilUserIsReady']); const mockOtherService = jasmine.createSpyObj('OtherService', ['doSomething']); beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [YourComponent], providers: [ { provide: YourService, useValue: mockService }, { provide: OtherService, useValue: mockOtherService } ] }).compileComponents(); // 模拟waitUntilUserIsReady,返回可手动控制的Promise mockService.waitUntilUserIsReady.and.callFake(() => { return new Promise<void>(resolve => { resolveWaitUntilReady = resolve; }); }); fixture = TestBed.createComponent(YourComponent); component = fixture.componentInstance; }); });
2. 验证Promise未决议时方法不执行
初始化组件后,直接断言doSomething未被调用:
it('should not call doSomething before Promise resolves', () => { fixture.detectChanges(); // 触发ngOnInit expect(mockOtherService.doSomething).not.toHaveBeenCalled(); });
3. 验证方法仅在Promise决议后执行
手动触发Promise决议,再断言方法被调用:
it('should call doSomething only after Promise resolves', () => { fixture.detectChanges(); // 决议前确认未调用 expect(mockOtherService.doSomething).not.toHaveBeenCalled(); // 手动触发Promise决议 resolveWaitUntilReady(); fixture.detectChanges(); // 触发变更检测(若需) // 决议后断言方法执行 expect(mockOtherService.doSomething).toHaveBeenCalled(); });
4. 直接测试原服务逻辑:控制Observable状态
若要测试原服务的waitUntilIsReady逻辑,可直接控制inProgressSubject的状态触发决议:
it('should resolve when inProgress becomes false', async () => { const service = TestBed.inject(YourService); // 访问私有subject(可通过类型断言或改为protected) const inProgressSubject = (service as any).inProgressSubject; let isResolved = false; service.waitUntilIsReady().then(() => isResolved = true); expect(isResolved).toBe(false); // 发送false触发决议 inProgressSubject.next(false); await fixture.whenStable(); // 等待微任务执行 expect(isResolved).toBe(true); });
5. 替代废弃的toPromise:使用firstValueFrom/lastValueFrom
若需创建延迟决议的Promise,改用RxJS的firstValueFrom或lastValueFrom:
import { firstValueFrom, of, delay } from 'rxjs'; // 创建延迟500ms决议的Promise const delayedPromise = firstValueFrom(of(undefined).pipe(delay(500)));
内容的提问来源于stack exchange,提问作者Tantol
相关产品推荐
相关产品推荐

