@jazim/mock-mongoose v1.0.1
Mock Mongoose
A Package for mocking Mongoose models that can be utilized with any node.js testing library such as Jest, Mocha, and Vitest etc ..
Installation
With NPM:
$ npm i @jazim/mock-mongoose -D
Import the library
const mockify = require('@jazim/mock-mongoose');
Usage
// 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);
mockify(Model).toReturn(obj, operation = 'find')
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);
});
});
});
mockify(Model).toReturn(fn, operation = 'find')
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);
});
});
});
mockify(Model).reset(operation = undefined)
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');
mockify.resetAll()
will reset all mocks.
beforeEach(() => {
mockify.resetAll();
});
Operations available:
find
- for find queryfindOne
- for findOne querycount
- for count query (deprecated)countDocuments
for count queryestimatedDocumentCount
for count collection documentsdistinct
- for distinct queryfindOneAndUpdate
- for findOneAndUpdate queryfindOneAndRemove
- for findOneAndRemove queryupdate
- for update query (DEPRECATED)updateOne
- for updateOne queryupdateMany
- for updateMany querysave
- for create, and save documentsModel.create()
orModel.save()
ordoc.save()
remove
- for remove query (DEPRECATED)deleteOne
- for deleteOne querydeleteMany
- for deleteMany queryaggregate
- for aggregate frameworkinsertMany
- forModel.insertMany()
bulk insert, can also pass{ lean: true, rawResult: true }
options.orFail
- for findOne().orFail() query or similar ones.
Notes
All operations work with exec
, promise
and callback
.
if you are using
Model.create
and 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.
deleteOne
andupdateOne
operation 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
.populate
in 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.
Recent Changes:
mockify.ModelName
is deprecated,mockify(Model)
is the now the recommended usage, withModel
being 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.