我刚刚从 3.4.0 更新到 3.5.1,并且在“观看”模型上的 Mocha 测试时,每次重新加载文件时,我都会从重新“要求”中收到 OverwriteModelError - 我猜是重新定义 - 模型。
使“覆盖”成为错误肯定有一些价值,但我现在要回到 3.4.0,因为这太痛苦了。
这是由编程错误引起的。 多次调用具有相同名称和架构的mongoose.model()
或connection.model()
不会导致错误。 传递具有不同架构的模型的名称会产生错误。 实际上从未允许覆盖模型。
如果 mocha watch 重新需要您的架构并调用mongoose.model(preExistingName, newSchema)
那么是的,您现在会收到错误消息。 以前在这种情况下,猫鼬根本没有编译新模型,它只会吞下编程错误并返回旧模型,这是错误的行为。
var schema = new Schema;
var A = mongoose.model('A', schema);
var B = mongoose.model('A', schema); // no error
var C = mongoose.model('A', new Schema); // previously unreported error
好吧,这是有道理的。 谢谢!
@aheckmann - 我也在打这个,但不确定我做错了什么。 下面是我如何设置它。 第一次干净的测试运行没问题,如果我编辑+保存文件,mocha 会触发--watch
重新运行,我得到
OverwriteModelError: Cannot overwrite "User" model once compiled.
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = mongoose.SchemaTypes.ObjectId;
var userSchema = new Schema({
name: { type: String }
}),
User;
// other virtual / static methods added to schema
User = mongoose.model('User', userSchema);
module.exports = User;
var User = require('../models/user'),
mongoose = require('mongoose');
mongoose.connection.on('error', function (err) {
console.log(err);
}
mongoose.connection.on('open', function () {
console.log('connected');
}
describe('User', function () {
before(function (done) {
mongoose.connect('mongodb://localhost/test');
done();
}
after(function (done) {
mongoose.disconnect(function () {
console.log('disconnected');
done();
});
});
it('should test something', function (done) {
// tests
done();
});
});
对于猫鼬测试,我们每次只创建新的连接,而且效果很好。
var db = mongoose.createConnection()
https://github.com/LearnBoost/mongoose/blob/master/test/common.js#L74 -L98
https://github.com/j0ni/mongoose-strangeness/blob/master/test.spec.js#L21
应该是var Example = connection.model('Example', ExampleSchema)
谢谢@aheckmann ,效果很好。
它可以工作 b/ca 为您的每个测试创建新连接,并且模型在每个连接中本地缓存。 另一种方法失败了 b/c 模型在每个测试的猫鼬模块级别进行编译,因此存在冲突。
@aheckmann - 尝试在Locomotive应用程序中测试我的模型时出现此错误。
基本上,每个测试文件都会在before()
函数中启动 Locomotive 应用程序。 启动应用程序会创建一个 mongo db 连接并加载到我的所有模型中 - 这在我的 mongoose 初始化文件中(在应用程序启动时运行一次):
var mongoose = require("mongoose"),
fs = require("fs");
module.exports = function() {
// Connect to DB
switch (this.env) {
case 'development':
mongoose.connect('mongodb://localhost/slipfeed');
break;
case 'test':
mongoose.connect('mongodb://localhost/slipfeed');
break;
case 'production':
mongoose.connect('mongodb://mongodb.example.com/prod');
break;
}
// Load models
var app = this,
modelPath = app.get('models');
fs.readdirSync(modelPath).forEach(function(file) {
var name = file.substr(0, file.lastIndexOf('.'));
require(modelPath + '/' + name)(mongoose, app);
});
}
在我的每个模型文件中,我基本上都是这样做的:
module.exports = function(mongoose, app) {
var Schema = mongoose.Schema;
var User = new Schema(...);
app.User = mongoose.model('User', User);
});
在我的每个模型测试文件中,我都在做这样的事情:
var locomotive = require("locomotive"),
app = new locomotive.Locomotive(),
should = require("should");
describe( "User", function() {
before( function (done) {
app.boot( process.cwd(), 'test', function () {
done();
});
});
after( function (done) {
mongoose.disconnect( function (err) {
if (err) throw err;
console.log('DISCONNECT')
done();
})
});
...tests go here
});
app.boot()
部分只是启动服务器,它加载配置文件并通过初始化程序运行(它们只是包含各种代码的文件,例如启动数据库连接)。
但是在我的第一个测试文件完成后,mocha 尝试加载下一个文件,我会得到OverwriteModelError
。
我知道这可能与加载下一个测试文件时没有关闭连接有关,或者我可能以错误的方式初始化我的模型。
无论如何,我尝试将mongoose.disconnect
到我的after()
函数中,但这并没有帮助。
它与打开或关闭的连接无关。 您正在尝试重新定义已存在于不同模式的模型。
var a = new Schema({ x: 'string' });
var b = new Schema({ x: 'string' });
mongoose.model('Thingy', a);
mongoose.model('Thingy', a); // ok, a === the registered schema instance
mongoose.model('Thingy', b); // error a !== the registered schema
FWIW,我认为一些稍微不那么琐碎的示例代码可以防止这种情况重复出现。 也许一个定义单个模型的小型 express 应用程序以可注入的方式初始化连接,以及一个 mocha 集成测试(在我的情况下,我使用的是 supertest,但不管是什么),它在不触发此问题的情况下注入连接。
如果有一些应用程序您知道我们可以查看 @aheckmann ,并且您认为它实现了规范模式,那么就可以解决问题。
这是我采取的措施:
https://github.com/j0ni/beachenergy.ca/blob/master/datamodel/index.js
我不喜欢它,但它使问题消失。 如果我使用的是猫鼬反模式(似乎很可能),那么规范示例会有所帮助。
嗨@aheckmann——我100%确定我没有创建一个具有不同模式的新模型。 我做了一个骨架/演示项目来说明这个问题: https :
用make
启动 mocha,第一次通过,测试将通过。 现在,编辑models/user.js
并保存(例如 CTRL/CMD + S),mocha 使用--watch
获取更改,我们点击OverwriteModelError
。
这是另一种选择......我使用nodemon。 这可确保每次测试运行时都会重新启动进程:
$ nodemon --exec "mocha -R min" 测试
那么推荐的解决方案是什么? 在每次测试之前创建一个新连接? 我们如何才能在我们的规范中定义 Mongoose 模型并且在观看时不会出错?
我放弃了单独用摩卡来解决它的尝试。 相反,我所做的是通过 nodemon 运行 mocha。 这样,节点进程重新启动,问题就消除了。
彼得·格拉夫
(从我的 iPad 发送,可能有拼写错误 :)
+1.512.784.3232
彼得。 [email protected]
2013 年 2 月 2 日上午 5:41,Oliver Joseph Ash [email protected]写道:
那么推荐的解决方案是什么? 在每次测试之前创建一个新连接? 我们如何才能在我们的规范中定义 Mongoose 模型并且在观看时不会出错?
—
直接回复此邮件或在 GitHub 上查看。
你是如何通过 nodemon 运行 mocha 的? 我试过这个,但我可能离得很远:
nodemon /usr/local/share/npm/bin/mocha --reporter spec --ui bdd --watch ./server/test/*.js
我用:
nodemon --exec "mocha -R min" 测试
鉴于您在下面显示的示例,您似乎使用规范而不是 min 并在服务器/测试中进行测试...如果是这样,只需更改参数...
2013 年 2 月 2 日上午 7:00,Oliver Joseph Ash [email protected]写道:
你是如何通过 nodemon 运行 mocha 的? 我试过这个,但我可能离得很远:
nodemon /usr/local/share/npm/bin/mocha --reporter spec --ui bdd --watch ./server/test/*.js
—
直接回复此邮件或在 GitHub 上查看。
好一个。 不如--watch
快,但更重要的是,它有效。
我还注意到,当对 Mocha 测试所需的架构进行更改时,Mocha 不会在它们被缓存时重新加载它们:(
在2013年2月2日,在15:23,皮特格拉夫[email protected]写道:
我用:
nodemon --exec "mocha -R min" 测试鉴于您在下面显示的示例,您似乎使用规范而不是 min 并在服务器/测试中进行测试...如果是这样,只需更改参数...
2013 年 2 月 2 日上午 7:00,Oliver Joseph Ash [email protected]写道:
你是如何通过 nodemon 运行 mocha 的? 我试过这个,但我可能离得很远:
nodemon /usr/local/share/npm/bin/mocha --reporter spec --ui bdd --watch ./server/test/*.js
—
直接回复此邮件或在 GitHub 上查看。—
直接回复此邮件或在 GitHub 上查看。
@j0ni的解决方案对我来说效果很好。 我最终将所有对“mongoose.model”的调用替换为:
try {
// Throws an error if "Name" hasn't been registered
mongoose.model("Name")
} catch (e) {
mongoose.model("Name", Schema)
}
我同意用这样的调用来乱扔代码并不好看。 最好有一个适用于最常见情况的简写,即模块只是在运行时重新解析而不更改架构。 也许称之为mongoose.getOrCreate(name, schema)
。
@yourcelf接受了您的建议并为我工作。 谢谢。
module.exports = getOrCreateModel('User', UserSchema);
还有另一种选择:从所有模型和模式中清除猫鼬。
在我的测试代码中,我添加了:
mongoose.models = {};
mongoose.modelSchemas = {};
它工作正常,至少对我来说。
@remicastaing完美,对我
@remicastaing 迄今为止最好的解决方法!
@remicastaing也对我
@remicastaing是的,看起来像一个
它仍然不知道实际问题是什么。
@remicastaing谢谢,也适用于我!
@remicastaing 也为我工作。 谢谢。
很抱歉划伤了旧痒,但我在尝试使用猫鼬运行 mocha -w 时遇到了同样的问题,尝试了所有可能的解决方案,但现在我遇到了麻烦。
我应该放在哪里
mongoose.models = {};
mongoose.modelSchemas = {};
?
之前,之后,之后,之前?
我应该在 beforeEach 上使用 mongoose.createConnection(),在 afterEach 上关闭吗?
我把
mongoose.models = {};
mongoose.modelSchemas = {};
紧随其后
var mongoose = require('mongoose');
在一个config.test.js
直接在文件中的test
文件夹( config.test.js
唯一*.test.js
在测试文件夹文件,其他*.test.js
文件是在子文件夹)。 正如摩卡去递归( --recursive
选项, mocha.opts
通过测试文件夹),它开始于config.test.js
。
@remicastaing谢谢! 也为我工作(茉莉花)
只是另一种情况:
当我需要在代码和测试中使用不同字母大小写的模型时,我遇到了这个问题。
例如在规范中:
var User = require('./models/user');
并在代码中var User = require('./models/User');
请记住,没有针对不同类型的缓存版本
+1 @asci 的经历。 如果您需要相同的模型但在两个不同的文件中使用不同的大小写,然后您需要另一个文件中的模型,则会引发“无法覆盖”错误
@iandoe我使用mongoose.connect
并进行 mocha 测试
after(function(done){
mongoose.models = {};
mongoose.modelSchemas = {};
mongoose.connection.close();
done();
});
:+1::弓:
:+1:
如果要在beforeEach
中创建模型,请先使用var db = mongoose.createConnection();
创建一个新连接,然后使用db.model(...)
而不是mongoose.model(...);
中的beforeEach
. 此行为是设计使然,而不是错误。
我有可以在运行时重新加载的代码,所以如果我在开发过程中更改架构或模型函数,我想覆盖现有模型。
除非有充分的理由不这样做,否则我不明白为什么我不能做这样的事情。
例子
delete mongoose.models['somemodel'];
var somemodel = db.mongoose.model('somemodel', someschema);
这对我有用,我已经这样做了大约一年。
或者也许有一种方法可以在不擦除现有模型/模式的情况下更改模式和模型功能,但我没有费心进一步调查。
@yourcelf是对的。
:+1:
我知道这是旧的,但如果有人偶然发现这个,我通过制作一个这样的数据库测试实用程序文件来解决它:
// DatabaseUtils.js
import mongoose from 'mongoose';
const MONGO_URI = 'mongodb://localhost:27017/collectionname'
export function tearDown(){
mongoose.models = {};
mongoose.modelSchemas = {};
resetDb();
if(mongoose.connection.readyState) mongoose.disconnect();
}
export function setUp(){
if(!mongoose.connection.readyState) mongoose.connect(MONGO_URI);
}
export function resetDb(){
if(mongoose.connection.db) mongoose.connection.db.dropDatabase();
}
然后从您的测试文件中,您可以使用:
import { setUp, tearDown, resetDb } from './DatabaseUtils'
describe('Testing MongoDB models', function(){
before(setUp)
after(tearDown)
afterEach(resetDb)
it('Some test of a mongoose model', () => {
// Test code here.
}
})
这个 StackOverflow 答案是这样解决的:
let users
try { users = mongoose.model('users') }
catch (e) { users = mongoose.model('users', <UsersSchema...>) }
编辑:正如@DanielRamosAcosta所指出的,“问题是,如果您的架构发生更改,更改将不会生效。”
谢谢@remicastaing ,我已经失去了 5 分钟,直到我找到你的解决方案,应该更明显:)
谢谢@juanpabloaj ,这对我有用🎉
@gunar问题是,如果您的架构发生更改,更改将不会生效。
@juanpabloaj谢谢兄弟!
我只是使用,而不是创建一个丑陋的 try/catch:
let users = mongoose.models.users || mongoose.model('users', <UsersSchema...>)
@tomyam1 ,也许是因为那些
有时,方便的解决方案比技术上正确的解决方案更有效:)
@tomyam1合作解决问题并没有什么可耻的,我感谢在这里发布自己的解决方案的每个人。 没有详细说明 - 在我的情况下,“不正确的hacky”建议之一效果很好,而您所谓的“正确”解决方案都没有。
互相帮助并不可耻。 感谢所有发表建设性帖子的人。
先生们(garethdown44,fega),那么我们不同意。
为避免制造更多垃圾邮件,请使用表情符号反应。
总结一下这个线程:
此页面中只有三种不同的解决方案:
mongoose.models.users || mongoose.model('users', <UsersSchema...>)
或者
try {
// Throws an error if "Name" hasn't been registered
mongoose.model("Name")
} catch (e) {
mongoose.model("Name", Schema)
}
这不是解决方案,因为它不会在模型中进行更改。
mongoose.models = {};
mongoose.modelSchemas = {};
尝试了这个解决方案,但没有用,谁知道为什么?
它依赖于修改未记录的内部变量。
它在 2013 年提出时可能确实有效,但已经是 4 年后的事了。
const Mongoose = require('mongoose');
const DB = Mongoose.createConnection(...);
const Model = DB.model("Name", schema);
@tomyam1 - 原则上同意你所说的。 然而,公平地说,该解决方案确实是在另一个(hacky)解决方案之后两年出现的!!
就我而言,我选择了 nodemon 解决方案。 当我意识到发生了什么以及测试代码的结构方式时,它需要进行大量重构,而我没有时间去做。 我很高兴提出了 nodemon 解决方案并且人们对它进行了 +1。 这就是我要说的。
可悲的是,Nodemon 重新启动了整个测试套件。 mocha --watch
重新加载速度要快得多
可悲的是,Nodemon 重新启动了整个测试套件,因此 mocha --watch 的重新加载速度要快得多
缓慢的重新加载和理智的开发人员是一种权衡,我将在测试运行中过度切片毫秒(或秒)。
此外,正如这里没有提到的, npm-watch
是一个执行此操作的工具(它包装了 nodemon),因此您可以定义一个简单的 npm 脚本watch
https://www.npmjs。 com/package/npm-watch
我有同样的问题,所以我检查mongoose.modelNames()
并确定我是编译模型还是只检索已经编译的模型,因为 mocha --watch 会导致这个问题。 所以这里是代码:
mongoose.modelNames().indexOf("User") === -1 //if my model has not been compiled...
? mongoose.model("User", UserSchema) //...compile model and return it
: mongoose.connection.model("User"); //else it is already compiled, so return this model
现在你将它作为一个函数返回(用参数替换“User”,用你的架构的参数替换 UserSchema)为module.exports
并且当你需要它时你调用这个函数。
@remicastaing 很有魅力,谢谢!
在某些特殊实例上,可能需要清除models
引用
after((done) => {
Object.keys(mongoose.models).forEach((k) => {
delete mongoose.models[k];
});
Object.keys(mongoose.modelSchemas).forEach((k) => {
delete mongoose.modelSchemas[k];
});
mongoose.connection.close();
done();
});
mocha --watch 不需要describe
之外的任何东西,即不会重新定义您的架构。
与数据库建立最少连接的最有效、最优雅且易于理解的测试方法是在测试套件之外设置连接、模式和模型。
与我在每次测试之前设置模型的其他示例相比,这段代码非常枯燥(对我来说不是必需的)。
以下测试套件有效:
const expect = require("chai").expect;
const mongoose = require("mongoose"),
UserSchema = require("../data/models/User");
const connection = mongoose.createConnection(
process.env.MONGO_URL || "mongodb://127.0.0.1/test"
);
const User = connection.model("User", UserSchema);
describe("Database Testing", function() {
it("MongoDB is working and repeatedly testable", function(done) {
let user = User({
username: "My user"
});
user
.save()
.then(doc => {
console.log(doc); // This outputs the doc.
expect(doc.username).to.equal("My user");
done();
})
.catch(err => {
console.error(err);
expect(err).to.be.null;
done();
});
});
});
从../data/models/User.js
let mongoose = require("mongoose");
let UserSchema = new mongoose.Schema({
username: String
});
module.exports = UserSchema; //
我曾经对 MongoDb 如何建立它的连接、模式和模型感到困惑。 我没有意识到您可以定义一个模型并多次使用它(显然,但是......)
我完全遵循文档并制作了一个定义架构并返回模型的模块。
这意味着如果我在测试中需要它,架构将被重新定义多次,因为它一直需要定义架构的模块。 Schema 的多重定义是没有道理的。
人们的解决方案(虽然有效,但效率较低)让您在每次测试之前创建一个新连接,并且通常在每次测试之前创建一个新模型。
但一个简单的理解是:
describe
之外的任何内容有效而优雅地解决了这个问题。
下面是我让这个工作效率较低的原始方法。
./test/db.spec.js
const expect = require("chai").expect;
const mongoose = require("mongoose"),
// mocha --watch does not rerequire anything
// outside of the test suite ("describe").
// Define your Schemas and models separately, so
// that you can define your Schema once above
// your test suites, and your models many times
// when testing.
UserSchema = require("../data/models/User");
describe("mongoose strangeness", function() {
var connection = mongoose.createConnection(
process.env.MONGO_URL || "mongodb://127.0.0.1/test"
);
// I tried the beforeEach and afterEach, but this wasn't needed:
// beforeEach(function(done) {
// connection = mongoose.createConnection(
// process.env.MONGO_URL || "mongodb://127.0.0.1/test"
// );
// connection.once("open", function() {
// done();
// });
// });
// afterEach(function(done) {
// connection.close(function() {
// done();
// });
// });
it("MongoDB testable", function(done) {
let User = connection.model("User", UserSchema);
let user = User({
username: "My user"
});
user
.save()
.then(doc => {
console.log(doc); // This outputs the doc every time.
expect(doc.username).to.equal("My user");
done();
})
.catch(err => {
console.error(err);
expect(err).to.be.null;
done();
});
});
});
快乐的 MongoDbing。
这段代码对我有用:
if (mongoose.modelNames().includes('Model')) {
mongoose.deleteModel('Model');
}
mongoose.model('Model', new mongoose.Schema({ ... }));
@verheyenkoen是对的。 这是 deleteModel 文档的链接: https: //mongoosejs.com/docs/api/connection.html#connection_Connection -deleteModel
最有用的评论
还有另一种选择:从所有模型和模式中清除猫鼬。
在我的测试代码中,我添加了:
它工作正常,至少对我来说。