Nest.js测试无法解析ReportingService依赖:Sequelize不可用
Nest.js测试依赖解析错误解决方案
问题根源
- 缺少Sequelize依赖提供者:
ReportingService构造函数中直接注入了Sequelize实例,但测试模块未提供该依赖的模拟实现,导致Nest无法解析依赖。 - 类属性初始化时机错误:
materialIdAttributes和materialIdOrder作为类级属性,会在构造函数执行前初始化,此时this.sequelize还未被注入,会直接抛出undefined错误,即便解决依赖注入也无法正常运行。
解决方案步骤
1. 为测试模块添加Sequelize模拟提供者
在reporting.service.spec.ts的测试模块中,补充Sequelize的模拟实现,确保Nest能解析该依赖:
import { getModelToken } from '@nestjs/sequelize'; import { Test } from '@nestjs/testing'; import { Sequelize } from 'sequelize-typescript'; import { MasterML } from '../../../src/simply/reporting/reporting.model'; import { ReportingService } from '../../../src/simply/reporting/reporting.service'; const testMaterialIds: string[] = [ 'MTRL-1', 'MTRL-2', 'MTRL-3', ]; // 模拟Sequelize核心方法 const mockSequelize = { fn: jest.fn((fnName, col) => `${fnName}(${col}`), literal: jest.fn((str) => str), col: jest.fn((colName) => colName), }; describe('ReportingService', () => { let service: ReportingService; let masterMlModel: typeof MasterML; beforeEach(async () => { const module = await Test.createTestingModule({ providers: [ ReportingService, { provide: getModelToken(MasterML), useValue: { // 修正模拟返回值,匹配服务中map操作的结构 findAll: jest.fn(() => testMaterialIds.map(id => ({ materialId: id }))), }, }, // 添加Sequelize模拟提供者 { provide: Sequelize, useValue: mockSequelize, }, ], }).compile(); service = module.get(ReportingService); masterMlModel = module.get<typeof MasterML>(getModelToken(MasterML)); }); it('Service should be defined', () => { expect(service).toBeDefined(); }); it('getMaterialIds - should return expected material IDs', async () => { const result = await service.getMaterialIds('PLNT1'); expect(result).toEqual(testMaterialIds); }); it('should call findAll with correct parameters', async () => { await service.getMaterialIds('PLNT1'); expect(masterMlModel.findAll).toHaveBeenCalledWith({ attributes: expect.any(Array), where: { plantId: 'PLNT1' }, order: expect.any(Array), }); }); });
2. 修复服务中类属性的初始化时机
将materialIdAttributes和materialIdOrder从类级属性改为构造函数内初始化,确保this.sequelize已注入后再创建配置:
import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/sequelize'; import { Sequelize } from 'sequelize-typescript'; import { MasterML } from './reporting.model'; @Injectable() export class ReportingService { materialIdAttributes: any[]; materialIdOrder: any[]; constructor( public sequelize: Sequelize, @InjectModel(MasterML) private readonly masterMl: typeof MasterML, ) { // 构造函数内初始化属性,此时this.sequelize已可用 this.materialIdAttributes = [ [ this.sequelize.fn('DISTINCT', this.sequelize.col('MATERIAL_ID')), 'materialId', ], ]; this.materialIdOrder = [[this.sequelize.literal('materialId ASC')]]; } async getMaterialIds( plantId: string, ): Promise<string[] | []> { return this.masterMl.findAll({ attributes: this.materialIdAttributes, where: { plantId }, order: this.materialIdOrder, }).then((rows) => rows.map((row) => row.materialId)); } }
额外说明
- 原测试中
findAll的模拟返回值不符合服务逻辑,改为返回包含materialId属性的对象数组,确保map操作能正确提取ID。 - 模拟
Sequelize的fn、literal、col方法,保证服务中的查询配置能正常生成。
内容的提问来源于stack exchange,提问作者Praneeth Karnena
相关产品推荐
相关产品推荐

