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

如何测试含async/await异步函数的组件?遇TypeError报错求助

解决TypeError: Cannot read properties of undefined (reading 'loadTodos')问题

问题根源

报错核心是组件内的testService实例未正确初始化,测试环境没给组件注入该服务的模拟实例,导致调用this.testService.loadTodos()时this.testService为undefined。另外你的测试用例还存在异步处理漏洞:直接调用component.loadTodos()后立即断言,会因为异步操作未完成导致断言失败。

分步解决方案

1. 正确创建并注入TestService的模拟Spy

在测试模块的TestBed配置中,先创建服务的spy对象,再将其作为依赖提供给组件:

// 测试文件顶部的配置部分
describe('YourComponent', () => {
  let component: YourComponent;
  let fixture: ComponentFixture<YourComponent>;
  let testServiceSpy: jasmine.SpyObj<TestService>;

  beforeEach(async () => {
    // 创建TestService的spy,指定要模拟的方法
    testServiceSpy = jasmine.createSpyObj('TestService', ['loadTodos']);

    await TestBed.configureTestingModule({
      declarations: [YourComponent],
      // 提供模拟的TestService
      providers: [
        { provide: TestService, useValue: testServiceSpy }
      ],
      // 若组件用到HttpClient,必须导入测试模块
      imports: [HttpClientTestingModule]
    })
    .compileComponents();

    fixture = TestBed.createComponent(YourComponent);
    component = fixture.componentInstance;
    // 此时组件的testService已被替换为我们的spy实例
  });
});

2. 修正测试用例的异步处理

因为loadTodos是异步函数,测试时必须等待其执行完成再断言,以下是两种常用实现方式:

方式一:使用async/await

it('should load todos correctly', async () => {
  const response2 = [
    {
      userId: 1,
      id: 1,
      title: 'delectus aut autem',
      completed: false,
    },
    {
      userId: 1,
      id: 2,
      title: 'quis ut nam facilis et officia qui',
      completed: false,
    },
  ];
  // 让spy返回一个已resolved的Promise
  testServiceSpy.loadTodos.and.resolveTo(response2);

  // 等待异步函数执行完成
  await component.loadTodos();

  expect(component.todos).toEqual(response2);
  // 可选:验证spy是否被调用过
  expect(testServiceSpy.loadTodos).toHaveBeenCalled();
});

方式二:使用fakeAsync/tick

it('should load todos correctly', fakeAsync(() => {
  const response2 = [
    {
      userId: 1,
      id: 1,
      title: 'delectus aut autem',
      completed: false,
    },
    {
      userId: 1,
      id: 2,
      title: 'quis ut nam facilis et officia qui',
      completed: false,
    },
  ];
  testServiceSpy.loadTodos.and.returnValue(Promise.resolve(response2));

  component.loadTodos();
  // 快进所有异步操作,模拟时间流逝
  tick();

  expect(component.todos).toEqual(response2);
  expect(testServiceSpy.loadTodos).toHaveBeenCalled();
}));

额外注意点

  • 若使用Jest而非Jasmine,创建spy的方式为const testServiceSpy = jest.mocked(jest.fn(() => ({ loadTodos: jest.fn() })));,核心思路一致:确保组件能获取到服务的模拟实例。
  • 服务中使用的toPromise()已被Angular弃用,建议改用lastValueFrom或firstValueFrom,但这不是当前报错的直接原因。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 22:15:34