A Package for mocking Mongoose models that can be utilized with any node.js testing library such as Jest, Mocha, and Vitest etc ..
With NPM:
$ npm i @jazim/mock-mongoose -Dconst mockify = require('@jazim/mock-mongoose');// user.js
const mongoose = require('mongoose');
const { Schema } = mongoose;
const schema = Schema({
name: String,
email: String,
created: { type: Date, default: Date.now },
});
module.exports = mongoose.model('User', schema);Returns a plain object.
// __tests__/user.test.js
const mockify = require('@jazim/mock-mongoose');
const model = require('./user');
describe('test mongoose User model', () => {
it('should return the doc with findById', () => {
const _doc = {
_id: '507f191e810c19729de860ea',
name: 'name',
email: 'name@email.com',
};
mockify(model).toReturn(_doc, 'findOne');
return model.findById({ _id: '507f191e810c19729de860ea' }).then((doc) => {
expect(JSON.parse(JSON.stringify(doc))).toMatchObject(_doc);
});
});
it('should return the doc with update', () => {
const _doc = {
_id: '507f191e810c19729de860ea',
name: 'name',
email: 'name@email.com',
};
mockify(model).toReturn(_doc, 'update');
return model
.update({ name: 'changed' }) // this won't really change anything
.where({ _id: '507f191e810c19729de860ea' })
.then((doc) => {
expect(JSON.parse(JSON.stringify(doc))).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.
You can use snapshots to automatically test that the queries sent out are valid.
// __tests__/user.test.js
const mockify = require('@jazim/mock-mongoose');
const model = require('./user');
describe('test mongoose User model', () => {
it('should return the doc with findById', () => {
const _doc = {
_id: '507f191e810c19729de860ea',
name: 'name',
email: 'name@email.com',
};
const finderMock = (query) => {
expect(query.getQuery()).toMatchSnapshot('findById query');
if (query.getQuery()._id === '507f191e810c19729de860ea') {
return _doc;
}
};
mockify(model).toReturn(finderMock, 'findOne'); // findById is findOne
return model.findById('507f191e810c19729de860ea').then((doc) => {
expect(JSON.parse(JSON.stringify(doc))).toMatchObject(_doc);
});
});
});will reset Model mock, if pass an operation, will reset only this operation mock.
it('should reset model mock', () => {
mockify(model).toReturn({ name: '1' });
mockify(model).toReturn({ name: '2' }, 'save');
mockify(model).reset(); // will reset all operations;
mockify(model).reset('find'); // will reset only find operations;
});you can also chain mockify#ModelName operations:
mockify(model)
.toReturn({ name: 'name' })
.toReturn({ name: 'a name too' }, 'findOne')
.toReturn({ name: 'another name' }, 'save')
.reset('find');will reset all mocks.
beforeEach(() => {
mockify.resetAll();
});-
find- for find query -
findOne- for findOne query -
count- for count query (deprecated) -
countDocumentsfor count query -
estimatedDocumentCountfor count collection documents -
distinct- for distinct query -
findOneAndUpdate- for findOneAndUpdate query -
findOneAndRemove- for findOneAndRemove query -
update- for update query (DEPRECATED) -
updateOne- for updateOne query -
updateMany- for updateMany query -
save- for create, and save documentsModel.create()orModel.save()ordoc.save() -
remove- for remove query (DEPRECATED) -
deleteOne- for deleteOne query -
deleteMany- for deleteMany query -
aggregate- for aggregate framework -
insertMany- forModel.insertMany()bulk insert, can also pass{ lean: true, rawResult: true }options. -
orFail- for findOne().orFail() query or similar ones.
All operations work with exec, promise and callback.
-
if you are using
Model.createand you don't pass a mock with mockify you'll receive the mongoose created doc (with ObjectId and transformations) -
validations are working as expected.
-
the returned document is an instance of mongoose Model.
-
deleteOneandupdateOneoperation returns original mocked object. -
you can simulate Error by passing an Error to mockify:
mockify(model).toReturn(new Error('My Error'), 'save'); return model .create({ name: 'name', email: 'name@email.com' }) .catch((err) => { expect(err.message).toBe('My Error'); });
-
you can mock
.populatein your mocked result just be sure to change theSchema's path to appropriate type (eg:Object|Mixed):User.schema.path('foreignKey', Object); const doc = { email: 'test@mail.com', foreignKey: { _id: '5ca4af76384306089c1c30ba', name: 'test', value: 'test', }, name: 'Name', saveCount: 1, }; mockify(User).toReturn(doc); const result = await User.find(); expect(result).toMatchObject(doc);
-
no connection is made to the database (mongoose.connect is mock function)
-
will work with node 6.4.x. tested with mongoose 4.x and jest 20.x and mocha.
-
check tests for more, feel free to fork and contribute.
-
mockify.ModelNameis deprecated,mockify(Model)is the now the recommended usage, withModelbeing a Mongoose model class.Alternatively, you may pass a string with the model name.
-
mockify(Model).toReturn((query) => value)can now take 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.
