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

如何用NestJS + Nest Crud编写服务测试?附报错咨询

解决NestJS中使用Crud库测试服务的错误问题

你遇到的两个错误本质都是因为TypeOrmCrudService初始化和运行时需要Repository具备更多元数据与方法支持,而你的Mock实现太简略,没覆盖这些必要内容。咱们一步步来修复:

错误原因拆解

  1. Cannot read property 'columns' of undefined:TypeOrmCrudService的构造函数会执行onInitMapEntityColumns方法,这个方法要读取repo.metadata.columns来映射实体字段,但你的Mock Repository没有提供metadata属性,导致初始化失败。
  2. Cannot read property 'save' of undefined:你的repositoryMockFactory返回的Mock对象没有把save方法初始化为jest的Mock函数,所以调用addressRepositoryMock.save.mockReturnValue时会报错。

具体解决方案

1. 完善Repository Mock工厂

给Mock的Repository补充TypeOrmCrudService必需的metadata属性,以及业务中用到的方法(比如save、createQueryBuilder):

// 替换你原来的repositoryMockFactory
export const repositoryMockFactory = () => ({
  // 初始化TypeOrmCrudService需要的元数据
  metadata: {
    columns: [], // 可以根据Address实体补充字段,空数组也能满足初始化要求
    relations: [],
  },
  // 把需要用到的Repository方法都声明为jest Mock函数
  save: jest.fn(),
  createQueryBuilder: jest.fn(() => ({
    select: jest.fn().mockReturnThis(),
    where: jest.fn().mockReturnThis(),
    getOne: jest.fn(),
    // 后续测试其他方法时,按需补充QueryBuilder的链式方法
  })),
});

2. 优化CrudRequest参数

虽然当前错误不来自这里,但createOne方法需要有效的CrudRequest参数,建议初始化一个更合规的对象:

const req: CrudRequest = {
  parsed: {
    fields: [],
    paramsFilter: [],
    query: {},
  },
  options: {
    routes: {},
    query: {},
  },
};

3. 调整测试用例逻辑

确保Mock方法的返回值设置正确,同时修正测试断言的细节:

修改后的完整测试类示例:

describe('AddressService', () => { 
  let module: TestingModule; 
  let addressService: AddressService; 
  let addressRepositoryMock: MockType<Repository<Address>>; 
  
  const req: CrudRequest = {
    parsed: { fields: [], paramsFilter: [], query: {} },
    options: { routes: {}, query: {} },
  }; 
  const createDto: Address = { /* 补充你的DTO字段 */ }; 
  const addressMock: Address = { /* 补充你的Mock实体字段 */ }; 

  const repositoryMockFactory = () => ({
    metadata: {
      columns: [],
      relations: [],
    },
    save: jest.fn(),
    createQueryBuilder: jest.fn(() => ({
      select: jest.fn().mockReturnThis(),
      where: jest.fn().mockReturnThis(),
      getOne: jest.fn(),
    })),
  });

  beforeEach(async () => { 
    module = await Test.createTestingModule({ 
      providers: [ 
        AddressService, 
        { provide: getRepositoryToken(Address), useFactory: repositoryMockFactory }, 
      ], 
    }).compile(); 

    addressService = module.get<AddressService>(AddressService); 
    addressRepositoryMock = module.get(getRepositoryToken(Address)); 
  }); 

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

  describe('create Address', () => { 
    it('should call addressRepository.save() and return the result', async () => { 
      // 异步方法要用mockResolvedValue,同步方法用mockReturnValue
      addressRepositoryMock.save.mockResolvedValue(addressMock);
      
      const result = await addressService.createOne(req, createDto); 
      
      expect(addressRepositoryMock.save).toHaveBeenCalledWith(createDto);
      expect(result).toEqual(addressMock); 
    }); 
  }); 
});

额外提示

  • 如果你的Address实体有自定义字段,metadata.columns可以补充对应字段的描述对象(比如{ propertyName: 'id', type: 'number' }),不过仅为了通过初始化的话,空数组完全够用。
  • 后续测试其他Crud方法(比如getOne、updateOne)时,需要在Mock工厂中添加对应的Repository方法(比如findOne、update)并设置Mock返回值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:40:32