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

Angular中如何在Jasmine里正确Mock Amplify进行单元测试?

如何正确Mock Amplify Auth解决Angular单元测试异步超时问题

别慌!刚上手单元测试碰到第三方库Mock的问题太正常了,我帮你捋清楚怎么正确Mock Amplify Auth来解决这个超时问题~

先说说你当前代码的问题

你遇到的Async function did not complete within 5000ms错误,核心原因有两个:

  1. 你Mock的Amplify.Auth.signIn直接返回了普通对象,但你的login方法用await等待它——await只对Promise生效,这个普通对象没法触发resolve,导致测试一直卡在等待状态。
  2. 你直接SpyOn了全局的Amplify.Auth,但TestBed里提供的是原始Amplify实例,两者没有关联,测试里的服务实际调用的还是未Mock的方法。

正确的Mock步骤

1. 创建Mock版的Amplify对象

我们需要让Auth.signIn返回一个resolved状态的Promise,同时把这个Mock对象注入到测试模块中:

// 定义Mock的Amplify,包含我们需要的Auth方法
const mockAmplify = {
  Auth: {
    signIn: jasmine.createSpy('signIn').and.returnValue(Promise.resolve({
      signInUserSession: { idToken: { jwtToken: 'tokenValue' } }
    }))
  }
};

如果你的项目用的是Jest,把jasmine.createSpy换成jest.fn(),returnValue换成mockResolvedValue即可。

2. 在TestBed中注入Mock的Amplify

不要直接提供原始的Amplify,而是用useValue替换成我们的Mock对象,同时记得调用compileComponents()(异步配置测试模块的必要步骤):

let myService: MyService;
let amplify: typeof Amplify;

beforeEach(async () => { 
  await TestBed.configureTestingModule({ 
    imports: [ HttpClientTestingModule ], 
    providers: [ 
      MyService,
      // 用Mock对象替代原始Amplify
      { provide: Amplify, useValue: mockAmplify } 
    ] 
  }).compileComponents(); 

  // 推荐用inject替代get,Angular 9+更推荐这个API
  myService = TestBed.inject(MyService);
  amplify = TestBed.inject(Amplify);
});

3. 调整测试用例,添加必要断言

现在测试可以正常执行了,我们还可以加上断言验证方法的调用逻辑:

it('should login successfully and trigger patientLogin with correct token', async () => { 
  // Mock私有方法patientLogin,避免实际发起HTTP请求
  spyOn(myService as any, 'patientLogin').and.returnValue(Promise.resolve(true));

  // 调用login方法
  const loginResult = await myService.login('USERNAME', 'password');

  // 验证Auth.signIn是否被正确调用(注意username被转成小写了)
  expect(amplify.Auth.signIn).toHaveBeenCalledWith('username', 'password');
  // 验证patientLogin是否传入了正确的token
  expect((myService as any).patientLogin).toHaveBeenCalledWith('tokenValue');
  // 验证login方法返回true
  expect(loginResult).toBe(true);
});

4. 处理patientLogin中的HTTP请求(可选但推荐)

如果你想完整测试整个登录流程,包括patientLogin里的HTTP请求,可以用HttpClientTestingModule的HttpTestingController来捕获并Mock响应:

let httpTestingController: HttpTestingController;

beforeEach(async () => { 
  await TestBed.configureTestingModule({ 
    imports: [ HttpClientTestingModule ], 
    providers: [ 
      MyService,
      { provide: Amplify, useValue: mockAmplify }
    ] 
  }).compileComponents(); 

  myService = TestBed.inject(MyService);
  amplify = TestBed.inject(Amplify);
  httpTestingController = TestBed.inject(HttpTestingController);
});

// 测试后验证所有HTTP请求都被处理,避免遗漏
afterEach(() => {
  httpTestingController.verify();
});

it('should complete full login flow including HTTP request', async () => { 
  const mockPatientToken = 'mock-patient-token';
  const mockLoginResponse = { token: mockPatientToken };

  // 调用login方法
  await myService.login('USERNAME', 'password');

  // 捕获sendRequest发出的HTTP请求(替换成你实际的接口URL)
  const request = httpTestingController.expectOne('/your-login-api-url');
  // 验证请求方法是否正确(假设是POST)
  expect(request.request.method).toBe('POST');
  // 返回Mock的响应
  request.flush(mockLoginResponse);

  // 验证服务的token是否被正确设置
  expect(myService['token']).toBe(mockPatientToken);
});

最后总结

核心就是要让Mock的signIn返回Promise,同时确保测试模块中使用的是Mock的Amplify实例,这样await就能正常获取返回值,不会导致超时。按照上面的步骤调整后,你的测试应该就能正常跑通啦~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 18:48:11