I am running node version 16.15.0 and have the package.json dependencies:
"jest": "^28.1.0",
"mongodb-memory-server": "^8.6.0",
"mongoose": "^6.3.5",
I have a mongoose.Schema set up in a User.js module:
import mongoose from 'mongoose'
import validator from 'validator'
import bcrypt from 'bcryptjs'
import jwt from 'jsonwebtoken'
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Please provide name'],
minlength: 3,
maxlength: 20,
trim: true,
},
email: {
type: String,
required: [true, 'Please provide email'],
validate: {
validator: validator.isEmail,
message: 'Please provide a valid email',
},
unique: true,
},
password: {
type: String,
required: [true, 'Please provide password'],
minlength: 6,
select: false,
},
role: {
type: String,
trim: true,
maxlength: 20,
default: 'PLAYER',
},
})
UserSchema.pre('save', async function () {
// console.log(this.modifiedPaths())
if (!this.isModified('password')) return
const salt = await bcrypt.genSalt(10)
this.password = await bcrypt.hash(this.password, salt)
})
UserSchema.methods.createJWT = function () {
return jwt.sign({ userId: this._id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_LIFETIME,
})
}
UserSchema.methods.comparePassword = async function (candidatePassword) {
const isMatch = await bcrypt.compare(candidatePassword, this.password)
return isMatch
}
export default mongoose.model('User', UserSchema)
(note the UserSchema.methods.createJWT = function
, because this is in the test below)
and finally a simple Jest test (I am only just starting out with Jest):
import mongoose from 'mongoose'
import dotenv from 'dotenv'
import { MongoMemoryServer } from 'mongodb-memory-server'
import User from '../../models/User.js'
describe('User Schema suite', () => {
dotenv.config()
const env = process.env
var con, mongoServer
beforeAll(async () => {
mongoServer = await MongoMemoryServer.create()
con = await mongoose.connect(mongoServer.getUri(), {})
jest.resetModules()
process.env = { ...env }
})
afterAll(async () => {
if (con) {
con.disconnect()
}
if (mongoServer) {
await mongoServer.stop()
}
process.env = env
})
test('should read the environment vars', () => {
expect(process.env.JWT_SECRET).toBeTruthy()
expect(process.env.JWT_SECRET).toEqual('?E(H+MbQeThWmYq3t6w9z$C&F)J@NcRf')
expect(process.env.JWT_LIFETIME).toBeTruthy()
expect(process.env.JWT_LIFETIME).toEqual('1d')
})
test('should create and sign a good token', async () => {
const user = await User.create({
name: 'Mike',
email: '[email protected]',
password: 'secret',
})
expect(user.createJWT()).toBeTruthy()
})
})
BTW: I also tried to add the _id
in manually with this User.create
expression
const user = await User.create({
_id: '62991d39873ec2778e34f114',
name: 'Mike',
email: '[email protected]',
password: 'secret',
})
But it made no difference.
The first test passes, however the second one fails with the following error:
mike@mike verser % npm test
> [email protected] test
> jest --testEnvironment=node --runInBand ./tests
FAIL tests/models/User.test.js
● User Schema suite › should create and sign a good token
TypeError: Cannot read properties of null (reading 'ObjectId')
at Object.<anonymous> (node_modules/mongoose/lib/types/objectid.js:13:44)
at Object.<anonymous> (node_modules/mongoose/lib/utils.js:9:18)
Test Suites: 1 failed, 1 skipped, 1 of 2 total
Tests: 1 failed, 1 skipped, 1 passed, 3 total
Snapshots: 0 total
Time: 1.127 s, estimated 2 s
Ran all test suites matching /./tests/i.
The code in User.js works in production where I am using Mongo Community v5.0.7 (in a docker container).
So why can’t I access the _id value when I am using MongoMemoryServer instead? Is there something that I need to set? Or is there something else that I’m doing wrong?
2
Answers
So, ultimately, the problem was that Jest couldn't handle the various arrow functions and imports I was making in my ES6 project. After going around at least a hundred different answers, involving babel or adding arguments to the Jest script call in package.json, and having none of them work except in really simple test cases...i gave up on Jest and have gone with Mocha. I'm getting a lot further now.
In short: if you're using ES6 - avoid Jest.
I'm told though, that there are various tools that can get TypeScript working with Jest, but I wasn't about to rewrite my whole project.
I was able to replicate it, it came from jest.config.js
I changed it from
to
And the error is gone.
You can give it a try using this test
if you use this jest configuration.
You will get TypeError: Cannot read properties of null (reading ‘ObjectId’)
For this example I used "jest": "^29.2.1"