A package for mocking Mongoose models — works with Jest and Vitest
npm i mockingoose -D// ESM / TypeScript
import mockingoose from 'mockingoose';
// CommonJS
const mockingoose = require('mockingoose');// user.ts
import mongoose, { Schema } from 'mongoose';
const schema = new Schema({
name: String,
email: String,
created: { type: Date, default: Date.now },
});
export default mongoose.model('User', schema);Returns a plain object.
// __tests__/user.test.ts
import mockingoose from 'mockingoose';
import User from './user';
describe('test mongoose User model', () => {
it('should return the doc with findById', async () => {
const _doc = {
_id: '507f191e810c19729de860ea',
name: 'name',
email: 'name@email.com',
};
mockingoose(User).toReturn(_doc, 'findOne');
const doc = await User.findById('507f191e810c19729de860ea');
expect(JSON.parse(JSON.stringify(doc))).toMatchObject(_doc);
});
it('should return the doc with updateOne', async () => {
const _doc = { ok: 1, nModified: 1, n: 1 };
mockingoose(User).toReturn(_doc, 'updateOne');
const result = await User.updateOne({ name: 'changed' }, {});
expect(result).toMatchObject(_doc);
});
});Allows passing a function in order to return the result.
You will be able to inspect the query using the parameter passed to the function. This will be either a Mongoose Query or Aggregate class, depending on your usage.
import mockingoose from 'mockingoose';
import User from './user';
describe('test mongoose User model', () => {
it('should return the doc with findById', async () => {
const _doc = {
_id: '507f191e810c19729de860ea',
name: 'name',
email: 'name@email.com',
};
const finderMock = (query: any) => {
if (query.getQuery()._id === '507f191e810c19729de860ea') {
return _doc;
}
};
mockingoose(User).toReturn(finderMock, 'findOne');
const doc = await User.findById('507f191e810c19729de860ea');
expect(JSON.parse(JSON.stringify(doc))).toMatchObject(_doc);
});
});Will reset Model mock. If passed an operation, will reset only that operation's mock.
mockingoose(User).toReturn({ name: '1' });
mockingoose(User).toReturn({ name: '2' }, 'save');
mockingoose(User).reset(); // will reset all operations
mockingoose(User).reset('find'); // will reset only find operationYou can also chain operations:
mockingoose(User)
.toReturn({ name: 'name' })
.toReturn({ name: 'a name too' }, 'findOne')
.toReturn({ name: 'another name' }, 'save')
.reset('find');Will reset all mocks.
beforeEach(() => {
mockingoose.resetAll();
});find- for find queryfindOne- for findOne querycountDocuments- for count queryestimatedDocumentCount- for count collection documentsdistinct- for distinct queryfindOneAndUpdate- for findOneAndUpdate queryfindOneAndDelete- for findOneAndDelete queryfindOneAndReplace- for findOneAndReplace queryupdateOne- for updateOne queryupdateMany- for updateMany querysave- for create, and save documentsModel.create()orModel.save()ordoc.save()deleteOne- for deleteOne querydeleteMany- for deleteMany queryaggregate- for aggregate frameworkreplaceOne- for replaceOne queryinsertMany- forModel.insertMany()bulk insert, can also pass{ lean: true, rawResult: true }options.bulkWrite- forModel.bulkWrite()bulk operationsbulkSave- forModel.bulkSave()bulk save
You can mock cursor() on find queries. The cursor uses the find mock data and supports next(), eachAsync(), close(), and for await...of:
mockingoose(User).toReturn([{ name: 'a' }, { name: 'b' }]);
// next()
const cursor = User.find().cursor();
const first = await cursor.next();
const second = await cursor.next();
const done = await cursor.next(); // null
// for await...of
for await (const doc of User.find().cursor()) {
console.log(doc);
}
// eachAsync
await User.find()
.cursor()
.eachAsync((doc) => {
console.log(doc);
});All operations work with exec and promise patterns.
-
If you are using
Model.createand you don't pass a mock with mockingoose you'll receive the mongoose created doc (with ObjectId and transformations) -
Validations work as expected.
-
The returned document is an instance of mongoose Model.
-
deleteOneandupdateOneoperations return the original mocked object. -
You can simulate errors by passing an Error to mockingoose:
mockingoose(User).toReturn(new Error('My Error'), 'save'); await expect( User.create({ name: 'name', email: 'name@email.com' }) ).rejects.toThrow('My Error');
-
You can mock
.populatein your mocked result — just change the Schema's path to appropriate type (e.g.,Object|Mixed):User.schema.path('foreignKey', Object); const doc = { email: 'test@mail.com', foreignKey: { _id: '5ca4af76384306089c1c30ba', name: 'test', value: 'test', }, name: 'Name', saveCount: 1, }; mockingoose(User).toReturn(doc); const result = await User.find(); expect(result).toMatchObject(doc);
-
You can mock
Model.exists()via thefindOneoperation. See Issue #69 -
No connection is made to the database (mongoose.connect is mocked)
-
Requires Node.js 18+ and Mongoose 9+. Tested with Vitest and Jest.
- Rewritten in TypeScript with full type exports
- Requires Node.js >= 18 and Mongoose >= 9
- Removed deprecated operations:
count,update,remove,findOneAndRemove - ESM + CJS dual package (use
importorrequire) - Tests use Vitest (Jest still supported at runtime)
-
mockingoose.ModelNameis deprecated,mockingoose(Model)is the recommended usage, withModelbeing a Mongoose model class.Alternatively, you may pass a string with the model name.
-
mockingoose(Model).toReturn((query) => value)can also take a function as a parameter.The function is called with either a Query or Aggregate object from Mongoose, depending on the request. This allows tests to ensure that proper queries are sent out, and helps with regression testing.
