测试集成MongoDB与Neo4J的NestJS服务时Jest报inspectModules未定义错误
解决Jest测试NestJS服务时的"TypeError: Cannot read properties of undefined (reading 'inspectModules')"错误
问题场景
我有一个同时操作MongoDB和Neo4J的UserService,其create方法会依次调用createUserInMongoDB和createUserInNeo4j方法,具体代码如下:
export class UserService { constructor( @InjectModel('User') private readonly userModel: Model<UserDocument>, @InjectModel('SportEvent') private readonly sportEventModel: Model<SportEventDocument>, private readonly neo4jQueryService: Neo4jQueryService ) {} //creating a user. async create(user: User): Promise<object> { let userInMongoDB: { mongoId: string; firstName: string }; try { userInMongoDB = await this.createUserInMongoDB(user); const userIncreaeNeo4j = await this.createUserInNeo4j( userInMongoDB.firstName, userInMongoDB.mongoId ); if (userIncreaeNeo4j && userInMongoDB) { return { statusCode: 201, message: `User succesfully created`, }; } } catch (error) { if (userInMongoDB) { await this.userModel.findByIdAndDelete(userInMongoDB.mongoId).lean(); } throw new HttpException(error.message, 400); } } //create user in mongoDB async createUserInMongoDB( user: User ): Promise<{ mongoId: string; firstName: string }> { try { const createdUser = new this.userModel(user); createdUser.email = user.email.toLowerCase(); createdUser.firstName = user.firstName.toLowerCase(); createdUser.lastName = user.lastName.toLowerCase(); console.log(user); await createdUser.save(); const userInMongoDB = { mongoId: createdUser._id.toString(), firstName: createdUser.firstName, }; return userInMongoDB; } catch (error) { if (error.code === 11000) { throw new HttpException( 'Duplicate entry, email has to be unique.', 409 ); } else { throw new HttpException(error.message, 400); } } } //create user in neo4j async createUserInNeo4j(userName: string, userId: string): Promise<boolean> { try { await this.neo4jQueryService.write( `CREATE (n:User {mongoId: \"${userId}\", name: \"${userName}\"}) RETURN n` ); return true; } catch (error) { throw new HttpException(error.message, 400); } } }
我编写了一个简单的Jest测试用例,仅验证userService是否已定义,测试代码如下:
import { Test, TestingModule } from '@nestjs/testing'; import { getModelToken } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { UserService } from '../user.service'; import { Neo4jQueryService } from '../../../neo4-j/neo4-j.service'; import { UserDocument } from '../../Schemas/user.schema'; import { User } from '@sport-activity-app/domain'; import { HttpException } from '@nestjs/common'; describe('UserService', () => { let userService: UserService; let userModel: Model<UserDocument>; let neo4jQueryService: Neo4jQueryService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ UserService, Neo4jQueryService, { provide: getModelToken('User'), useValue: { // mock Mongoose Model methods used in the UserService class findByIdAndDelete: jest.fn(), create: jest.fn(), }, }, { provide: getModelToken('SportEvent'), useValue: { // mock Mongoose Model methods used in the UserService class }, }, ], }).compile(); userService = module.get<UserService>(UserService); userModel = module.get<Model<UserDocument>>(getModelToken('User')); neo4jQueryService = module.get<Neo4jQueryService>(Neo4jQueryService); }); it('should be defined', () => { expect(userService).toBeDefined(); }); });
但运行测试时抛出错误:TypeError: Cannot read properties of undefined (reading 'inspectModules')
解决方案
错误原因
测试用例中直接注册了真实的Neo4jQueryService,而该服务在测试环境中可能依赖未初始化的Neo4j驱动、配置项或其他外部资源,导致实例化失败,进而触发inspectModules相关的未定义错误。
修复步骤
将真实的Neo4jQueryService替换为Mock实现,只模拟UserService中用到的write方法即可:
- 在测试模块的
providers数组中,替换Neo4jQueryService为Mock对象 - 确保Mock对象包含
write方法(用jest.fn()模拟)
修改后的完整测试代码
import { Test, TestingModule } from '@nestjs/testing'; import { getModelToken } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { UserService } from '../user.service'; import { Neo4jQueryService } from '../../../neo4-j/neo4-j.service'; import { UserDocument } from '../../Schemas/user.schema'; import { User } from '@sport-activity-app/domain'; import { HttpException } from '@nestjs/common'; describe('UserService', () => { let userService: UserService; let userModel: Model<UserDocument>; let neo4jQueryService: jest.Mocked<Neo4jQueryService>; beforeEach(async () => { // 创建Neo4jQueryService的Mock实现 const mockNeo4jQueryService = { write: jest.fn().mockResolvedValue(true), } as jest.Mocked<Neo4jQueryService>; const module: TestingModule = await Test.createTestingModule({ providers: [ UserService, // 注册Mock服务,替换真实的Neo4jQueryService { provide: Neo4jQueryService, useValue: mockNeo4jQueryService, }, { provide: getModelToken('User'), useValue: { findByIdAndDelete: jest.fn().mockResolvedValue(true), // 模拟构造函数和save方法,适配createUserInMongoDB的调用 constructor: jest.fn().mockImplementation((user) => ({ ...user, email: user.email.toLowerCase(), firstName: user.firstName.toLowerCase(), lastName: user.lastName.toLowerCase(), save: jest.fn().mockResolvedValue({ _id: 'mock-mongo-id', firstName: user.firstName.toLowerCase(), }), })), }, }, { provide: getModelToken('SportEvent'), useValue: {}, }, ], }).compile(); userService = module.get<UserService>(UserService); userModel = module.get<Model<UserDocument>>(getModelToken('User')); neo4jQueryService = module.get(Neo4jQueryService); }); it('should be defined', () => { expect(userService).toBeDefined(); }); });
额外注意点
- 若
Neo4jQueryService使用自定义注入token(如@Inject('NEO4J_SERVICE')),需将Mock的provide值改为对应的token字符串 - 补充了
userModel的构造函数和save方法Mock,避免后续测试create方法时因调用真实Mongoose逻辑报错 - 所有外部依赖都应使用Mock替代,确保测试不依赖真实服务或数据库
内容的提问来源于stack exchange,提问作者MisterQvD
相关产品推荐
相关产品推荐

