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

Angular与Jasmine集成测试中Service触发延迟问题求助

问题:集成测试中确保Service在断言前触发值

组件代码

export class HomePage implements OnInit {
  public flag = false;

  constructor(private authService: AuthService) {
    console.log("HomePage Created");
    this.authService.subject.subscribe(res => {
      this.flag = res;
    })
  }

  ngOnInit() {
  }
}

服务代码

export class AuthService {
  public subject = new BehaviorSubject(false);

  constructor(private storage: Storage) {
    console.log("AuthService created");
    this.storage.create().then(() => {
      this.storage.set('token', true).then(() => {
        this.storage.get('token').then(token => {
          console.log("token sent");
          this.subject.next(token);
        })
      })
    });
  }
}

测试代码

describe('HomePage', () => {
  let component: HomePage;
  let fixture: ComponentFixture<HomePage>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [IonicStorageModule.forRoot()],
    });
    fixture = TestBed.createComponent(HomePage)
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  fit('should create', async () => {
    await fixture.whenStable()
    console.log("test run");
    expect(component).toBeTruthy();
  });
});

测试输出

24 05 2024 17:19:33.830:INFO [Chrome 115.0.0.0 (Linux x86_64)]: Connected on socket ZveuSQt62spdgsS6AAAF with id 45284113
LOG: 'AuthService created'
Chrome 115.0.0.0 (Linux x86_64): Executed 0 of 4 SUCCESS (0 secs / 0 secs)
LOG: 'HomePage Created'
Chrome 115.0.0.0 (Linux x86_64): Executed 0 of 4 SUCCESS (0 secs / 0 secs)
LOG: 'test run'
Chrome 115.0.0.0 (Linux x86_64): Executed 0 of 4 SUCCESS (0 secs / 0 secs)
LOG: 'token sent'
Chrome 115.0.0.0 (Linux x86_64): Executed 1 of 4 (skipped 3) SUCCESS (0.056 secs / 0.04 secs)
LOG: 'token sent'
TOTAL: 1 SUCCESS

问题说明

从测试输出可见,token sent在测试运行完成后才打印。实际场景中,组件因未在测试断言前收到token,导致DOM组件无法更新。需要在不Mock服务的前提下,确保Service在断言执行前触发值,尝试过多种fixture.whenStable()和fixture.detectChanges()的组合均无效。


解决方案

核心问题是Ionic Storage的异步操作属于微任务队列,而fixture.whenStable()仅等待Angular自身的异步操作完成,不会主动清空所有微任务队列。以下是几种无需Mock服务的集成测试解决方案:

1. 手动触发微任务执行

在await fixture.whenStable()后,通过Promise.resolve()清空微任务队列,再触发变更检测:

fit('should create and receive token', async () => {
  await fixture.whenStable();
  // 等待所有微任务(包括Storage操作)完成
  await Promise.resolve();
  // 触发变更检测更新DOM
  fixture.detectChanges();
  console.log("test run");
  expect(component.flag).toBe(true);
  expect(component).toBeTruthy();
});

2. 在beforeEach中等待Service的信号

通过监听AuthService的BehaviorSubject,等待其发出预期值后再初始化组件:

beforeEach(async () => {
  await TestBed.configureTestingModule({
    imports: [IonicStorageModule.forRoot()],
  }).compileComponents();
  
  fixture = TestBed.createComponent(HomePage);
  component = fixture.componentInstance;
  const authService = TestBed.inject(AuthService);
  // 等待subject发送token值
  await new Promise(resolve => {
    const sub = authService.subject.subscribe(value => {
      if (value === true) {
        sub.unsubscribe();
        resolve(value);
      }
    });
  });
  fixture.detectChanges();
});

fit('should create', async () => {
  console.log("test run");
  expect(component.flag).toBe(true);
  expect(component).toBeTruthy();
});

3. 修改Service暴露初始化Promise

如果允许修改服务代码,将构造函数内的异步逻辑封装为公开Promise,方便测试等待:

// 修改后的AuthService
export class AuthService {
  public subject = new BehaviorSubject(false);
  public initPromise: Promise<void>;

  constructor(private storage: Storage) {
    console.log("AuthService created");
    this.initPromise = this.storage.create().then(() => {
      return this.storage.set('token', true).then(() => {
        return this.storage.get('token').then(token => {
          console.log("token sent");
          this.subject.next(token);
        });
      });
    });
  }
}

测试中直接等待该Promise:

beforeEach(async () => {
  await TestBed.configureTestingModule({
    imports: [IonicStorageModule.forRoot()],
  }).compileComponents();
  
  fixture = TestBed.createComponent(HomePage);
  component = fixture.componentInstance;
  const authService = TestBed.inject(AuthService);
  await authService.initPromise;
  fixture.detectChanges();
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 14:57:03