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

Jest全模拟测试中出现循环结构转JSON错误排查

问题分析与解决方案

这个错误的根源是Angular的Zone和Jest异步处理机制冲突:Jest在处理异步测试时会尝试序列化相关上下文对象,而Zone内部存在循环引用结构,触发了JSON.stringify失败。再加上测试代码里的冗余写法,最终导致了报错。

1. 移除测试中冗余的.then()调用

你代码里的await component.isKontoValid().then()是完全多余的写法,.then()不带回调会直接返回原Promise,改成直接await即可:

const result = await component.isKontoValid();

2. 用Jest假定时器规避Zone干扰

你的isKontoValid函数使用了setTimeout(即便没有设置延迟),Angular的Zone会自动包裹这个定时器,导致Jest处理时触发循环引用序列化错误。改用Jest假定时器可以彻底规避这个问题:

it('should return false if hasSepaEinwilligung is false', async () => {
  // 启用Jest假定时器
  jest.useFakeTimers();
  
  // 模拟依赖对象(确保无循环引用)
  component['kontoComponent'] = { form: { invalid: false } };
  component['dataService'].data.checkout.konto.hasSepaEinwilligung = false;
  
  // 调用目标函数,获取未resolve的Promise
  const validationPromise = component.isKontoValid();
  // 执行所有待处理的定时器任务
  jest.runAllTimers();
  
  // 等待Promise完成并断言结果
  const result = await validationPromise;
  expect(result).toBeFalsy();
  
  // 恢复真实定时器,避免影响其他测试
  jest.useRealTimers();
});

3. 确保模拟对象无循环引用

不要直接使用Angular真实的FormControl/FormGroup对象作为模拟,这类对象内部会关联Zone或其他存在循环引用的结构。直接用简单字面量模拟即可:

// 模拟合法表单状态
component['kontoComponent'] = { form: { invalid: false } };
// 模拟非法表单状态
component['kontoComponent'] = { form: { invalid: true } };

可选优化:简化原函数的异步逻辑

如果你的isKontoValid函数不需要真实的延迟,只是为了返回Promise,可以直接用Promise.resolve替代setTimeout,从根源避免Zone的干扰:

isKontoValid(): Promise<boolean> {
  return Promise.resolve(
    !this.kontoComponent.form.invalid &&
    this.dataService.data.checkout.konto.hasSepaEinwilligung
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 17:05:06