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

NestJS+Jest中expect().toHaveReturnedWith()断言返回空对象问题

NestJS + Jest 中 toHaveReturnedWith() 断言失败问题

在NestJS项目中使用Jest测试时,遇到expect().toHaveReturnedWith()断言异常:通过jest.spyOn(subscriptionsHistoryRepository, 'findAll').mockResolvedValue([mockRecords, 2])设置mock返回值后,调试可见findAll()确实返回预期的mockRecords,但用expect(subscriptionsHistoryRepository.findAll).toHaveReturnedWith([mockRecords, 2])断言时收到空对象{},其余断言均正常通过。

测试代码

describe('SubscriptionsModule', () => {
  describe('GetSubscriptionHistoryService', () => {
    let service: GetSubscriptionHistoryService;
    let subscriptionsHistoryRepository: SubscriptionsHistoryRepository;

    beforeAll(async () => {
      const moduleRef: TestingModule = await Test.createTestingModule({
        providers: [
          GetSubscriptionHistoryService,
          {
            provide: SubscriptionsHistoryRepository,
            useValue: {
              findAll: jest.fn(),
            },
          },
        ],
      }).compile();

      service = moduleRef.get<GetSubscriptionHistoryService>(GetSubscriptionHistoryService);
      subscriptionsHistoryRepository = moduleRef.get<SubscriptionsHistoryRepository>(
        SubscriptionsHistoryRepository,
      );
    });

    afterEach(() => {
      jest.resetAllMocks();
    });

    it('Should have all class instances defined', () => {
      expect(service).toBeDefined();
      expect(subscriptionsHistoryRepository).toBeDefined();
    });

    it("Should return the subscription's status history", async () => {
      jest.spyOn(subscriptionsHistoryRepository, 'findAll').mockResolvedValue([mockRecords, 2]);

      await service.execute(CUSTOMER_ID, SUBSCRIPTION_ID, 1);

      expect(subscriptionsHistoryRepository.findAll).toHaveBeenCalledTimes(1);
      expect(subscriptionsHistoryRepository.findAll).toHaveBeenCalledWith({
        where: {
          customerId: CUSTOMER_ID,
          subscriptionId: SUBSCRIPTION_ID,
        },
        take: 5,
        skip: 0,
        order: {
          createdAt: 'DESC',
        },
      });
      expect(subscriptionsHistoryRepository.findAll).toHaveReturnedWith([mockRecords, 2]);
    });
  });
});

测试结果

expect(jest.fn()).toHaveReturnedWith(expected)

Expected: [[{"createdAt": 2023-11-30T02:02:51.982Z, "customerId": "f1e8fc48-4780-45e4-858b-9e15b3382db5", "deletedAt": null, "extProviderStatus": "trialing", "id": "2e4c42b1-3d46-4438-bc68-64de46aa16d0", "status": "started", "subscriptionId": "2e4c42b1-3d46-4438-bc68-64de46aa16d5", "updatedAt": 2023-11-30T02:02:51.982Z}, {"createdAt": 2023-11-30T02:02:51.982Z, "customerId": "f1e8fc48-4780-45e4-858b-9e15b3382db5", "deletedAt": null, "extProviderStatus": "active", "id": "2e4c42b1-3d46-4438-bc68-64de46aa16d1", "status": "active", "subscriptionId": "2e4c42b1-3d46-4438-bc68-64de46aa16d5", "updatedAt": 2023-11-30T02:02:51.982Z}], 2]

Received: {}

原因分析

toHaveReturnedWith()断言的是同步函数的直接返回值,但findAll是异步方法,实际返回的是一个Promise对象,而非你通过mockResolvedValue设置的resolved结果。Jest在记录异步mock函数的返回值时,会将Promise对象的状态记录下来,但toHaveReturnedWith无法解析Promise,所以拿到的是空对象(Promise的内部状态快照)。

解决方案

方案1:断言业务方法的返回值(推荐)

直接测试service.execute()的返回结果,这更贴合业务测试的核心目标:

it("Should return the subscription's status history", async () => {
  jest.spyOn(subscriptionsHistoryRepository, 'findAll').mockResolvedValue([mockRecords, 2]);

  const result = await service.execute(CUSTOMER_ID, SUBSCRIPTION_ID, 1);

  expect(subscriptionsHistoryRepository.findAll).toHaveBeenCalledTimes(1);
  expect(subscriptionsHistoryRepository.findAll).toHaveBeenCalledWith({
    where: {
      customerId: CUSTOMER_ID,
      subscriptionId: SUBSCRIPTION_ID,
    },
    take: 5,
    skip: 0,
    order: {
      createdAt: 'DESC',
    },
  });
  // 直接断言service的返回值
  expect(result).toEqual([mockRecords, 2]);
});

方案2:检查mock函数的resolved结果

如果一定要验证mock函数的resolved值,可以通过mock.results获取并解析Promise:

it("Should return the subscription's status history", async () => {
  const mockFindAll = jest.spyOn(subscriptionsHistoryRepository, 'findAll').mockResolvedValue([mockRecords, 2]);

  await service.execute(CUSTOMER_ID, SUBSCRIPTION_ID, 1);

  expect(subscriptionsHistoryRepository.findAll).toHaveBeenCalledTimes(1);
  expect(subscriptionsHistoryRepository.findAll).toHaveBeenCalledWith({
    where: {
      customerId: CUSTOMER_ID,
      subscriptionId: SUBSCRIPTION_ID,
    },
    take: 5,
    skip: 0,
    order: {
      createdAt: 'DESC',
    },
  });
  // 取出mock的resolved值并断言
  const resolvedValue = await mockFindAll.mock.results[0].value;
  expect(resolvedValue).toEqual([mockRecords, 2]);
});

方案3:放弃toHaveReturnedWith

明确toHaveReturnedWith不适用于异步函数,改用上述两种方式替代。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 20:25:58