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

如何在Karma/Jasmine中测试普通函数内Promise抛出的错误?

解决异步Promise中抛出错误的单元测试问题

首先要先指出一个你可能忽略的细节:你的AuthHelperMock里的sendSignInLink方法条件逻辑有误!当前的if (email != undefined || email != "")会导致空字符串也会进入resolve分支(因为空字符串不等于undefined,条件始终为true),永远不会触发reject。你需要把逻辑改成&&才能正确判断有效邮箱:

public sendSignInLink(email: String): Promise<any> {
  return new Promise((resolve, reject) => {
    if (email !== undefined && email !== "") resolve(1); 
    else reject("Invalid email");
  });
}

回到你的核心问题:因为doLogin里的错误是在Promise的catch回调中异步抛出的,同步的expect(() => component.doLogin()).toThrowError会在微任务执行前就结束,自然抓不到错误。下面是两种不需要修改原doLogin函数的测试方案:

方案1:使用Angular的fakeAsync + tick 捕获全局未处理Promise拒绝

利用Angular测试工具的fakeAsync可以模拟异步时序,tick()会触发所有待处理的微任务,再结合监听全局的unhandledrejection事件来捕获抛出的错误:

import { fakeAsync, tick } from '@angular/core/testing';

it('should throw EmailSignInLinkError on invalid email', fakeAsync(() => {
  component.email = "";
  let caughtError: Error | undefined;

  // 临时替换全局未处理Promise拒绝的处理函数
  const originalUnhandledRejection = window.onunhandledrejection;
  window.onunhandledrejection = (event) => {
    caughtError = event.reason as Error;
    event.preventDefault(); // 阻止测试因未捕获错误而失败
  };

  component.doLogin();
  tick(); // 触发所有微任务,执行catch里的throw逻辑

  // 验证错误类型和信息
  expect(caughtError).toBeInstanceOf(EmailSignInLinkError);
  expect(caughtError?.message).toBe("invalid Mail");

  // 恢复原来的处理函数,避免影响其他测试
  window.onunhandledrejection = originalUnhandledRejection;
}));

方案2:修改Mock跟踪Promise,结合async/await

给你的AuthHelperMock添加一个属性来跟踪sendSignInLink返回的Promise,这样测试代码可以直接等待这个Promise完成,再捕获异步抛出的错误:

export class AuthHelperMock {
  public lastSignInPromise: Promise<any> | undefined;

  public sendSignInLink(email: String): Promise<any> {
    this.lastSignInPromise = new Promise((resolve, reject) => {
      if (email !== undefined && email !== "") resolve(1);
      else reject("Invalid email");
    });
    return this.lastSignInPromise;
  }
}

然后在测试中:

it('should throw EmailSignInLinkError on invalid email', async () => {
  component.email = "";
  const authMock = TestBed.inject(AuthHelper) as AuthHelperMock;
  let caughtError: Error | undefined;

  // 监听未处理的Promise拒绝
  const originalUnhandledRejection = window.onunhandledrejection;
  window.onunhandledrejection = (event) => {
    caughtError = event.reason as Error;
    event.preventDefault();
    window.onunhandledrejection = originalUnhandledRejection;
  };

  component.doLogin();
  // 等待sendSignInLink的Promise完成
  await authMock.lastSignInPromise;
  // 触发微任务执行catch里的throw
  await Promise.resolve();

  expect(caughtError).toBeInstanceOf(EmailSignInLinkError);
  expect(caughtError?.message).toBe("invalid Mail");
});

这两种方案都不需要修改原doLogin函数的结构,就能正确捕获异步抛出的错误。

内容的提问来源于stack exchange,提问作者Benjamin Schröder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:13:04