如何用NestJS + Nest Crud编写服务测试?附报错咨询
解决NestJS中使用Crud库测试服务的错误问题
你遇到的两个错误本质都是因为TypeOrmCrudService初始化和运行时需要Repository具备更多元数据与方法支持,而你的Mock实现太简略,没覆盖这些必要内容。咱们一步步来修复:
错误原因拆解
Cannot read property 'columns' of undefined:TypeOrmCrudService的构造函数会执行onInitMapEntityColumns方法,这个方法要读取repo.metadata.columns来映射实体字段,但你的Mock Repository没有提供metadata属性,导致初始化失败。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
相关产品推荐
相关产品推荐

