diff --git a/lib/helpers/populate/getModelsMapForPopulate.js b/lib/helpers/populate/getModelsMapForPopulate.js index 41b92097419..dbe3707766a 100644 --- a/lib/helpers/populate/getModelsMapForPopulate.js +++ b/lib/helpers/populate/getModelsMapForPopulate.js @@ -389,8 +389,7 @@ function _virtualPopulate(model, docs, options, _virtualRes) { let foreignField = virtual.options.foreignField; if (!localField || !foreignField) { - return new MongooseError('If you are populating a virtual, you must set the ' + - 'localField and foreignField options'); + return new MongooseError(`Cannot populate virtual \`${options.path}\` on model \`${model.modelName}\`, because options \`localField\` and / or \`foreignField\` are missing`); } if (typeof localField === 'function') { diff --git a/test/helpers/getModelsMapForPopulate.test.js b/test/helpers/getModelsMapForPopulate.test.js new file mode 100644 index 00000000000..5b2d6632f68 --- /dev/null +++ b/test/helpers/getModelsMapForPopulate.test.js @@ -0,0 +1,41 @@ +'use strict'; + +const assert = require('assert'); +const start = require('../common'); +const util = require('../util'); + +const mongoose = start.mongoose; +const Schema = mongoose.Schema; + +describe('getModelsMapForPopulate', function() { + let db; + + beforeEach(() => db.deleteModel(/.*/)); + + before(function() { + db = start(); + }); + + after(async function() { + await db.close(); + }); + + afterEach(() => util.clearTestData(db)); + afterEach(() => util.stopRemainingOps(db)); + + it('should error on missing options on populate', async function() { + const sch = new Schema({ + test: mongoose.Schema.Types.ObjectId + }, { + virtuals: { + someVirtual: {} + } + }); + + const model = db.model('Test', sch); + + const doc = await model.create({ test: new mongoose.Types.ObjectId() }); + + await assert.rejects(() => model.findById(doc._id).populate('someVirtual').exec(), /Cannot populate virtual `someVirtual` on model `Test`, because options `localField` and \/ or `foreignField` are missing/); + }); +});