【发布时间】:2016-01-21 02:53:30
【问题描述】:
给定一个简单的 Mongoose 模型:
import mongoose, { Schema } from 'mongoose';
const PostSchema = Schema({
title: { type: String },
postDate: { type: Date, default: Date.now }
}, { timestamps: true });
const Post = mongoose.model('Post', PostSchema);
export default Post;
我想测试这个模型,但遇到了一些障碍。
我目前的规范看起来像这样(为简洁起见省略了一些内容):
import mongoose from 'mongoose';
import { expect } from 'chai';
import { Post } from '../../app/models';
describe('Post', () => {
beforeEach((done) => {
mongoose.connect('mongodb://localhost/node-test');
done();
});
describe('Given a valid post', () => {
it('should create the post', (done) => {
const post = new Post({
title: 'My test post',
postDate: Date.now()
});
post.save((err, doc) => {
expect(doc.title).to.equal(post.title)
expect(doc.postDate).to.equal(post.postDate);
done();
});
});
});
});
但是,每次运行测试时我都会访问我的数据库,我希望避免这种情况。
我尝试过使用Mockgoose,但我的测试无法运行。
import mockgoose from 'mockgoose';
// in before or beforeEach
mockgoose(mongoose);
测试卡住并抛出错误消息:Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test. 我已尝试将超时时间增加到 20 秒,但没有解决任何问题。
接下来,我扔掉了 Mockgoose 并尝试使用 Sinon 来 stub save 调用。
describe('Given a valid post', () => {
it('should create the post', (done) => {
const post = new Post({
title: 'My test post',
postDate: Date.now()
});
const stub = sinon.stub(post, 'save', function(cb) { cb(null) })
post.save((err, post) => {
expect(stub).to.have.been.called;
done();
});
});
});
这个测试通过了,但不知何故对我来说没有多大意义。我对存根,嘲笑,你有什么很陌生,......我不确定这是否是正确的方法。我在post 上存根save 方法,然后我断言它已被调用,但我显然在调用它......另外,我似乎无法理解非-stubbed Mongoose 方法将返回。我想将post 变量与save 方法返回的变量进行比较,就像在我访问数据库的第一个测试中一样。我试过couple 和methods,但他们都觉得很hackish。必须有一个干净的方法,不是吗?
几个问题:
我真的应该避免像我一直阅读的那样访问数据库吗?我的第一个示例运行良好,每次运行后我都可以清除数据库。但是,我感觉不太对。
如何从 Mongoose 模型中存根保存方法并确保它实际测试我想要测试的内容:将新对象保存到数据库。
【问题讨论】:
-
如果你是一个 mockist TDDer,Oleg 的答案看起来不错,但大多数经典 TDDer 会have no problem with hitting the database。有关 mock、stub 和 mockist 与经典 TDD 的解释,请参阅有关该主题的 Martin Fowler's article。
-
@heenenee 归根结底,测试是为了保证代码质量,所以只要质量不受影响,就没有写入或错误。基本单元测试对 DB 的缺点是:(1) 速度,(2) CI 和单个项目开发人员的复杂性,(3) 通过 DB 状态转移的测试副作用,在单个测试或同时测试运行之间,这很难解决,(4)修复错误意味着开发人员需要付出额外的努力,在最坏的情况下是外部资源依赖。这一切都没有错,但实际上仅适用于集成测试。我会明确地将两者分开。
-
@heenenee 忘了提一下:感谢 Martin Fowler 的一篇非常有趣的文章的链接!
-
@Oleg 我同意所有这些。就个人而言,我没有对与持久性相关的代码进行单元测试而只进行集成测试很好。这源于经典 TDD 的“尽可能使用真实对象”的心态。我只是想向提问者提供这种观点和一些背景知识,因为他说他是存根/模拟的新手,可能根本不了解集成测试。
标签: node.js mongodb unit-testing mongoose sinon