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

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()
  })
}

核心疑问

  1. 如何编写测试验证doSomething()仅在Promise决议后执行,而非之前?
  2. 如何在测试中手动控制Promise的决议时机(类似通过Observable Subject调用next()的方式)?
  3. 曾尝试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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 02:44:53