84

我将如何模拟我的 node.js 应用程序中的数据库,在这种情况下,它mongodb用作博客 REST API 的后端?

当然,我可以将数据库设置为特定的testing数据库,但我仍然会保存数据并且不仅测试我的代码,还测试数据库,所以我实际上不是在进行单元测试而是进行集成测试。
那么一个人应该怎么做呢?创建数据库包装器作为应用程序和数据库之间的中间层并在测试时替换 DAL?

// app.js  
var express = require('express');
    app = express(),
    mongo = require('mongoskin'),
    db = mongo.db('localhost:27017/test?auto_reconnect');

app.get('/posts/:slug', function(req, res){
    db.collection('posts').findOne({slug: req.params.slug}, function (err, post) {
        res.send(JSON.stringify(post), 200);
    });
});

app.listen(3000);

// test.js
r = require('requestah')(3000);
describe("Does some testing", function() {

  it("Fetches a blogpost by slug", function(done) {
    r.get("/posts/aslug", function(res) {
      expect(res.statusCode).to.equal(200);
      expect(JSON.parse(res.body)["title"]).to.not.equal(null);
      return done();
    });

  });
));
4

6 回答 6

137

我认为如果不使用数据库软件对其进行测试,就无法正确测试与数据库相关的代码。那是因为您要测试的代码不仅是 javascript,而且是数据库查询字符串。即使在您的情况下,查询看起来很简单,您也不能永远依赖它。

所以任何数据库模拟层都必须实现整个数据库(也许减去磁盘存储)。到那时,您最终会使用数据库模拟器进行集成测试,即使您将其称为单元测试。另一个缺点是,与数据库相比,数据库模拟器最终可能会出现一组不同的错误,并且您可能最终不得不为数据库模拟器和数据库编写代码(有点像 IE、Firefox 和 Chrome 等的情况。 )。

因此,在我看来,正确测试代码的唯一方法是将其与真实数据库连接。

于 2012-09-21T08:17:44.317 回答
46

关于模拟,有一个一般的经验法则是

不要嘲笑任何你不拥有的东西。

如果要模拟数据库,请将其隐藏在抽象服务层后面并模拟该层。然后确保您集成测试实际的服务层。

就我个人而言,我已经不再使用模拟进行测试,而是将它们用于自上而下的设计,帮助我从上到下推动开发,同时模拟服务层,然后最终实现这些层并编写集成测试。用作测试工具时,它们往往会使您的测试变得非常脆弱,并且在最坏的情况下会导致实际行为和模拟行为之间出现分歧。

于 2012-09-25T09:39:56.113 回答
44

到目前为止,我不同意所选答案或其他答复。

如果您能够在进入 QA 之前捕获由对 DB 模式和您的代码所做的混乱且多次混乱的更改所产生的错误,那不是很棒吗?我敢打赌,大多数人会大喊是的!

您当然可以而且应该隔离和测试您的数据库模式。而且您不会基于模拟器或重图像或数据库和机器的重新创建来执行此操作。这就是像 SQLite 这样的东西的一个例子。您基于内存中轻量级实例运行并使用在内存实例中不会更改的静态数据来模拟它,这意味着您真正在隔离测试您的数据库,并且您也可以信任您的测试。显然它很快,因为它在内存中,是一个骨架,并且在测试运行结束时被废弃。

所以是的,您应该并且您应该测试导出到您正在使用的任何数据库引擎/运行时的非常轻量级的内存实例中的 SCHEMA,并且随着添加非常少量的静态数据成为您隔离的模拟数据库。

您可以定期(以自动方式)从真实数据库中导出真实模式,并在每次推送到 QA 之前将它们导入/更新到内存数据库实例中,您将立即知道数据库管理员或其他人是否完成了任何最新的数据库更改最近更改架构的开发人员破坏了所有测试。

虽然我为尽力回答的努力鼓掌,但如果可以的话,我会否决当前的答案,但我是新手,还没有建立足够的声誉来使我有能力这样做。

至于回答“不要嘲笑你不拥有的东西”的人。我认为他的意思是“不要测试你不拥有的任何东西”。但是你会嘲笑你不拥有的东西!因为那些不是需要隔离的东西!

我计划与您分享如何使用真实示例 JS 代码在未来的某个时间点更新这篇文章!

这是许多测试驱动团队一直在做的事情。你只需要了解如何。

于 2015-05-26T23:56:59.840 回答
5

我对任何语言的 DB 代码进行单元测试的首选方法是通过 Repository 抽象访问 Mongo(这里有一个示例http://iainjmitchell.com/blog/?p=884)。实现会因暴露的 DB 特定功能而异,但通过从您自己的逻辑中删除所有 Mongo 代码,您就可以进行单元测试。只需将 Mongo Repository 实现替换为非常简单的存根版本即可。例如,只需将对象存储在一个简单的内存字典集合中。

您将获得以这种方式对自己的代码进行单元测试而不依赖于数据库的好处,但您仍然需要对主数据库进行集成测试,因为您可能永远无法像其他人那样模拟真实数据库的特质说到这里。我发现的事情就像在安全模式下索引与在没有安全模式下索引一样简单。具体来说,如果您有一个唯一索引,那么您的虚拟内存实现可能会在所有情况下都尊重这一点,但 Mongo 不会没有安全模式。

因此,虽然您仍然需要针对 DB 进行某些操作的测试,但您当然可以使用已存根的 Repository 实现正确地对您自己的逻辑进行单元测试。

于 2012-09-30T09:39:29.410 回答
4

模拟的目的是跳过复杂性和单元测试自己的代码。如果您想编写 e2e 测试,请使用 db.

编写代码来设置/拆除用于单元测试的测试数据库是技术债务并且令人难以置信的不满意。

npm 中有模拟库:

蒙哥 - https://www.npmjs.com/package/mongomock

猫鼬 - https://www.npmjs.com/package/mockgoose

如果那些不支持您需要的功能,那么是的,您可能需要使用真实的东西。

于 2017-03-13T04:07:18.683 回答
3

我遇到了这个困境,并选择使用测试数据库并在每次测试开始时清理它。(如何删除所有内容:https ://stackoverflow.com/a/25639377/378594 )

使用 NPM,您甚至可以创建一个测试脚本来创建 db 文件并在之后清理它。

于 2016-09-04T07:58:23.830 回答