Jest使用MongoMemoryServer/mongoose时无法读取Schema _id报错
问题根因
报错TypeError: Cannot read properties of null (reading 'ObjectId')的核心原因是测试执行阶段mongoose连接实例的db属性尚未初始化,值为null,你在createJWT方法中大概率写了从mongoose.connection.db上解构读取ObjectId的逻辑。
生产环境下服务启动流程会严格等待mongoose连接完全就绪后才开始处理业务请求,因此不会触发该问题;但Jest测试中如果没有完整等待MongoMemoryServer启动、mongoose连接建立完成就执行模型操作,就会命中这个null引用错误,这和你创建用户时传不传_id参数没有关系——报错点根本不在实例字段读取上,而在方法内部依赖的ObjectId对象来源为空。
修复步骤
- 补全beforeAll生命周期的异步等待逻辑,强制指定MongoMemoryServer的二进制版本和生产环境对齐,避免版本兼容问题
正确的测试初始化代码参考:const { MongoMemoryServer } = require('mongodb-memory-server'); const mongoose = require('mongoose'); const User = require('./User'); let mongoServer; beforeAll(async () => { // 等待内存Mongo实例启动,指定和生产一致的5.0.7版本,避免默认拉取最新版MongoDB带来的驱动兼容问题 mongoServer = await MongoMemoryServer.create({ binary: { version: '5.0.7' } }); const mongoUri = mongoServer.getUri(); // 等待mongoose连接完全建立,禁止省略await await mongoose.connect(mongoUri); // 增加连接状态校验,确保连接就绪后再跑用例:1代表已连接 if (mongoose.connection.readyState !== 1) { throw new Error('mongoose connect failed before test run'); } }); // 测试结束后清理连接和实例,避免测试进程挂死 afterAll(async () => { await mongoose.disconnect(); await mongoServer.stop(); }); - 修正
createJWT方法中的ObjectId引用方式,不要从连接实例上读取ObjectId,彻底移除对连接初始化状态的依赖
错误写法(触发报错的根源):
正确写法:// 连接未就绪时mongoose.connection.db值为null,解构时直接抛出你遇到的错误 const { ObjectId } = mongoose.connection.db; userSchema.methods.createJWT = function () { // 业务逻辑中用到了上述ObjectId做类型校验 return jwt.sign({ userId: this._id }, process.env.JWT_SECRET); };// 直接从mongoose顶层Types对象上取ObjectId,不需要依赖数据库连接状态 const { ObjectId } = mongoose.Types; userSchema.methods.createJWT = function () { // 如果只是读取实例的_id生成token,根本不需要额外引入ObjectId,直接读取this._id即可 return jwt.sign( { userId: this._id }, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRES_IN } ); }; - 处理Jest模块缓存导致的模型绑定异常:如果测试中存在多次引入模型、重复连接的逻辑,在beforeEach中清理已注册的模型,避免拿到未绑定有效连接的模型副本
beforeEach(() => { Object.keys(mongoose.models).forEach(modelName => { delete mongoose.models[modelName]; }); // 清理后再重新引入User模型即可 });
验证标准
修复完成后,先单独运行创建用户的用例,确认三点即可解决问题:
- beforeAll执行完成后,mongoose连接状态为1(已连接)
- User.create返回的用户实例可以直接读取到
_id字段值 - 调用实例的createJWT方法时不再抛出null引用错误
内容的提问来源于stack exchange,提问作者Michael Coxon
相关产品推荐
相关产品推荐

