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

Jest单元测试中无法mock nestjs-typeorm-paginate的paginate函数

问题背景

我需要对服务的findAll方法做单元测试mock,目前有两种思路:要么mock myEntityRepository的findAndCount仓库方法,要么mock nestjs-typeorm-paginate npm包的paginate函数。其中findAll方法负责从数据表拉取记录列表,且使用了nestjs-typeorm-paginate模块实现分页能力。


尝试方案1:mock myEntityRepository

执行时报错,错误栈如下:

TypeError: queryBuilder.limit is not a function

  at ../node_modules/nestjs-typeorm-paginate/dist/paginate.js:119:28
  at ../node_modules/nestjs-typeorm-paginate/dist/paginate.js:8:71
  at Object.<anonymous>.__awaiter (../node_modules/nestjs-typeorm-paginate/dist/paginate.js:4:12)
  at paginateQueryBuilder (../node_modules/nestjs-typeorm-paginate/dist/paginate.js:115:12)
  at Object.<anonymous> (../node_modules/nestjs-typeorm-paginate/dist/paginate.js:22:15)
  at ../node_modules/nestjs-typeorm-paginate/dist/paginate.js:8:71

my.service.ts代码:

import { IPaginationOptions, paginate, Pagination } from 'nestjs-typeorm-paginate'

export class MyService {
  constructor(@InjectRepository(MyEntity) private myEntityRepository: Repository<MyEntity>) { }

  async findAll(options: IPaginationOptions): Promise<Pagination<MyEntity>> {
    try {
      return await paginate<MyEntity>(this.myEntityRepository, options)
    } catch (error) {
      throw error
    }
  }
}

my.service.spec.ts代码:

describe('MyService Basic GET findAll test cases', () => {
  let service: MyService
  let repositoryMock: MockType<Repository<MyEntity>>

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [MyService,
        {
          provide: getRepositoryToken(MyEntity), useFactory: repositoryMockFactory
        }
      ],
    }).compile()

    service = module.get<MyService>(MyService)
    repositoryMock = module.get(getRepositoryToken(MyEntity))

    const itemList = [{
        id: 1,
        my_field: 'a1',
    }, {
        id: 2,
        my_field: 'a2',
    }, ]
  })

  it('should findAll() the MyEntity', async () => {
    expect((await service.findAll(options)).items.length).toBe(itemsList.length)
  })
})

const repositoryMockFactory: () => MockType<Repository<MyEntity>> = jest.fn(() => ({
  find: jest.fn(entity => entity),
  findAndCount: jest.fn(entity => entity),
}))

方案1报错原因

nestjs-typeorm-paginate的paginate方法接收Repository实例时,会优先尝试调用createQueryBuilder方法构建查询,你mock的Repository只实现了find和findAndCount,缺少createQueryBuilder方法的实现,所以内部生成的queryBuilder对象没有limit等方法,才会抛出该错误。


尝试方案2:mock paginate方法

修改后仍然抛出错误:

TypeError: Cannot redefine property: paginate at Function.defineProperty (<anonymous>)

修改后的my.service.spec.ts代码:

import * as nestjsTypeormPaginate from 'nestjs-typeorm-paginate' // imported at top
  ....
  ....
  it('should findAll() the MyEntity', async () => {
    const queryDto: QueryMyEntityDto = { customerId: 1 }
    const options: IPaginationOptions = { page: 1, limit: 10 }
    let paginationMock = jest.spyOn(nestjsTypeormPaginate, 'paginate')
    paginationMock.mockImplementation((dto, options) => Promise.resolve({
      items: itemList.slice(0, 2),
      meta: {
        itemCount: 2,
        totalItems: 2,
        totalPages: 1,
        currentPage: 1,
      }
    }))
    repositoryMock.find.mockReturnValue(itemList)
    expect((await service.findAll(options)).items.length).toBe(itemsList.length)
  })
  ...

方案2报错原因

nestjs-typeorm-paginate导出的paginate属性默认是不可配置的,jest.spyOn无法直接修改不可配置的属性,所以抛出重定义错误。


解决方案

两种可行实现任选即可:

方案A:补全Repository的mock方法(适配原方案1)

paginate方法如果检测到Repository存在findAndCount方法,就会优先使用该方法而不调用createQueryBuilder,你只需要把mock的findAndCount返回值改成符合TypeORM规范的格式即可:

// 修改repositoryMockFactory的实现
const repositoryMockFactory: () => MockType<Repository<MyEntity>> = jest.fn(() => ({
  findAndCount: jest.fn().mockResolvedValue([
    // 第一个元素是查询到的实体列表
    [{ id: 1, my_field: 'a1' }, { id: 2, my_field: 'a2' }],
    // 第二个元素是总记录数
    2
  ]),
}))

修改后不需要额外mock其他方法,直接执行测试即可正常运行。

方案B:在jest配置层面mock整个paginate模块(适配原方案2)

不要用spyOn的方式修改单个导出属性,直接在测试文件顶部用jest.mock mock整个模块:

// 在测试文件import语句的最上方添加
jest.mock('nestjs-typeorm-paginate', () => ({
  ...jest.requireActual('nestjs-typeorm-paginate'),
  paginate: jest.fn(),
}));
import * as nestjsTypeormPaginate from 'nestjs-typeorm-paginate';

// 测试用例里直接指定mock返回值即可
it('should findAll() the MyEntity', async () => {
  const options: IPaginationOptions = { page: 1, limit: 10 };
  const itemList = [{ id: 1, my_field: 'a1' }, { id: 2, my_field: 'a2' }];
  (nestjsTypeormPaginate.paginate as jest.Mock).mockResolvedValue({
    items: itemList,
    meta: {
      itemCount: 2,
      totalItems: 2,
      totalPages: 1,
      currentPage: 1,
    }
  });
  expect((await service.findAll(options)).items.length).toBe(itemList.length);
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 18:54:05