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

Jest多it块中httpService Mock行为未更新问题排查求助

解决Jest多测试块中HttpService模拟失效问题

问题根源

  1. Mock结构不匹配AxiosResponse类型:你构造的mock响应仅包含data字段,但HttpService的post方法返回的是Observable<AxiosResponse>,AxiosResponse要求包含status、headers等必填属性,结构不匹配会引发类型兼容问题,干扰后续逻辑执行。
  2. 直接赋值替换mock而非使用Jest spy机制:通过httpService.post = jest.fn()直接替换方法,这类mock不受Jest的resetAllMocks()管理,容易导致测试间的mock残留。

修复步骤

  1. 模拟完整的AxiosResponse结构:确保mock响应完全符合AxiosResponse的类型定义,包含所有必要属性。
  2. 使用jest.spyOn创建受控mock:借助Jest的spy机制管理mock,配合resetAllMocks()可保证每个测试块的mock相互独立。
  3. 利用RxJS的of创建Observable:HttpService基于RxJS,直接用of()生成符合类型的Observable,避免手动构造toPromise对象的冗余操作。

修复后的代码示例

import { of } from 'rxjs';
import { AxiosResponse } from 'axios';

// ... 其他导入代码

describe('CPDCService', () => {
  let module: TestingModule;
  let service: CPDCService;

  beforeEach(async () => {
    module = await Test.createTestingModule({
      providers: [CPDCService, ...providersMock]
    }).compile();

    service = module.get<CPDCService>(CPDCService);
    jest.resetAllMocks(); // 重置所有mock,确保测试隔离
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  describe('submit', () => {
    it('should throw BadRequestException when Recaptcha token is invalid', async () => {
      const httpService = module.get<HttpService>(HttpService);
      const cpdcData: CPDCDto = {
        formData: {
          recaptchaToken: 'mockRecaptchaToken',
          email: 'test@example.com'
        },
        campaignId: 'mockCampaignId',
        bountyId: 'mockBountyId',
        formId: 'mockFormId',
        referrer: 'google.com',
        deviceId: 'mockDeviceId'
      };

      // 模拟完整的AxiosResponse结构
      const recaptchaResponse: AxiosResponse = {
        data: { success: false },
        status: 200,
        statusText: 'OK',
        headers: {},
        config: {}
      };

      // 使用spyOn创建mock,返回符合类型的Observable
      jest.spyOn(httpService, 'post').mockReturnValue(of(recaptchaResponse));

      await expect(service.submit(cpdcData, true, undefined, 'en')).rejects.toThrow(
        BadRequestException
      );
    });

    it('should throw NotFoundException when campaign is not found', async () => {
      const httpService = module.get<HttpService>(HttpService);
      const cpdcData: CPDCDto = {
        formData: {
          recaptchaToken: 'mockRecaptchaToken',
          email: 'test@example.com'
        },
        campaignId: 'mockCampaignId',
        bountyId: 'mockBountyId',
        formId: 'mockFormId',
        referrer: 'google.com',
        deviceId: 'mockDeviceId'
      };

      // 模拟完整的AxiosResponse结构
      const recaptchaResponse: AxiosResponse = {
        data: { success: true },
        status: 200,
        statusText: 'OK',
        headers: {},
        config: {}
      };

      // 使用spyOn创建独立的mock
      jest.spyOn(httpService, 'post').mockReturnValue(of(recaptchaResponse));

      await expect(service.submit(cpdcData, true, undefined, 'en')).rejects.toThrow(
        NotFoundException
      );
    });
  });
});

额外说明

  • 若项目中RxJS版本不支持直接使用of(),可手动构造Observable,但需确保包含toPromise方法且返回正确的AxiosResponse。
  • jest.resetAllMocks()会重置所有mock的调用次数和实例状态,但不会恢复原始方法;若需要完全恢复,可在每个测试块结束后调用spy.mockRestore()。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 07:47:29