NestJS控制器测试Mock服务未生效:为何仍调用原RoomService?
问题
刚接触NestJS,正在做控制器测试。RoomController依赖RoomService,RoomService依赖Mongoose的RoomModel,而RoomModel引入了其他模块的实体。测试时已经通过useValue注入Mock的RoomService,但运行测试报错:
Cannot find module 'src/modules/building/models/building.model' from 'modules/room/models/room.model.ts'
看起来测试仍在加载原RoomService相关的依赖,试了多种Mock注入方式都没用,求问题原因。
相关代码
RoomController代码
@Controller('rooms') export class RoomController { constructor(private readonly roomService: RoomService) {} @Get() async findAll(): Promise<ReadRoomDto[]> { return await this.roomService.findAll(); } }
RoomService代码
@Injectable() export class RoomService { constructor( @InjectModel(Room.name) private readonly roomModel: RoomModel, ) {} async findAll():Promise<ReadRoomDto[]> { return await this.roomModel.find(); } }
RoomModel代码
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { Document, Model, Types } from 'mongoose'; import { Building } from 'src/modules/building/models/building.model'; import { Floor } from 'src/modules/floor/entities/floor.entity'; import { Organization } from 'src/modules/organization/models/organization.model'; import { Property } from 'src/modules/property/models/property.model'; interface RoomAttrs { name: string; floor?: string; building?: string; organization?: string; properties?: string[]; } export interface RoomModel extends Model<Room> { build(attrs: RoomAttrs): Room; } @Schema() export class Room extends Document { @Prop() name: String; @Prop({ type: String, required: false, ref: Floor.name }) floor: String; @Prop({ type: String, required: false, ref: Building.name }) building: String; @Prop({ type: String, required: false, ref: Organization.name }) organization: String; @Prop([{ type: String, required: false, ref: Property.name }]) properties: String[]; } export const RoomSchema = SchemaFactory.createForClass(Room); RoomSchema.set('toJSON', { transform: (doc, ret) => { ret.id = doc._id; delete ret._id; }, }); RoomSchema.statics.build = function (attrs: RoomAttrs) { return new this(attrs); };
测试代码
import { Test, TestingModule } from '@nestjs/testing'; import { RoomController } from '../controllers/room.controller'; import { RoomStub } from './stubs/room.stub'; import { RoomService } from '../services/room.service'; const mockRoomService = { findAll: jest.fn().mockResolvedValue([RoomStub()]), }; describe('RoomController', () => { let roomController: RoomController; //let roomService :RoomService beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [RoomController], providers: [{provide:RoomService,useValue:mockRoomService}], }).compile(); roomController = module.get<RoomController>(RoomController); //roomService = module.get<RoomService>(RoomService) }); it('should be defined', () => { expect(roomController).toBeDefined(); }); });
解决方案
问题根源
你遇到的不是Mock注入失效,而是TypeScript模块路径解析错误,加上测试时依赖加载的连锁反应:
- RoomModel里用了
src/...开头的绝对导入路径,但测试运行时,从modules/room/models/room.model.ts的位置无法正确解析src/开头的路径; - 虽然你Mock了RoomService,但测试代码里import了原RoomService文件,TypeScript会加载它的依赖链(RoomService→RoomModel→其他模块实体),触发了错误的路径解析。
解决步骤
1. 修复模块导入路径
两种方式二选一:
- 改用相对路径:根据实际目录结构,把RoomModel中的绝对导入改成相对路径,比如:
// 原路径 import { Building } from 'src/modules/building/models/building.model'; // 改为相对路径(示例,根据你的目录层级调整) import { Building } from '../../building/models/building.model'; - 配置路径别名:在
tsconfig.json中添加路径映射,确保绝对路径能被正确解析:
同时在{ "compilerOptions": { "baseUrl": "./", "paths": { "src/*": ["src/*"] } } }jest.config.js中同步配置Jest的路径映射:module.exports = { // 其他配置项 moduleNameMapper: { '^src/(.*)$': '<rootDir>/src/$1', }, };
2. 彻底隔离依赖(可选)
如果不想加载原RoomService的任何依赖,可以用jest.mock直接Mock整个RoomService模块,代替测试模块中的useValue注入:
import { Test, TestingModule } from '@nestjs/testing'; import { RoomController } from '../controllers/room.controller'; import { RoomStub } from './stubs/room.stub'; import { RoomService } from '../services/room.service'; // 直接Mock整个RoomService模块 jest.mock('../services/room.service', () => ({ RoomService: jest.fn().mockImplementation(() => ({ findAll: jest.fn().mockResolvedValue([RoomStub()]), })), })); describe('RoomController', () => { let roomController: RoomController; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [RoomController], providers: [RoomService], // 直接使用Mock后的服务 }).compile(); roomController = module.get<RoomController>(RoomController); }); it('should be defined', () => { expect(roomController).toBeDefined(); }); });
内容的提问来源于stack exchange,提问作者simple-User
相关产品推荐
相关产品推荐

