在node.js中模拟数据库?


78

我如何在我的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();
    });

  });
));

Answers:


126

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

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

因此,我认为,正确测试代码的唯一方法是将其与真实数据库接口。


1
你知道,你说的很对。尽管单元测试具有惊人的目的(即隔离),但是您已经为集成测试奠定了基础。
Mike Perrenoud 2015年

4
@MichaelPerrenoud:我喜欢christkv的回答所提出的规则:“不要嘲笑您不拥有的任何东西”。尽管没有详细说明为什么这是一个坏主意,但这是一个容易记住的规则。
slebetman

1
我不同意这个答案,在meteorjs中,他们在运行测试时以某种方式设置了测试数据库(我假设它不是模拟库,而是临时文件),并且非常方便。具有完全像mongodb一样的行为并自行清理的对象将非常有用。实现细节是全部在内存中还是临时文件中,因此您不必重复代码。我确实同意,制作驱动程序的人应该是制作模拟对象的人。
Uri 2016年

我想我也不同意这个答案。OP的问题很简单,因为它只是一个控制器和数据库。那么使服务器成为服务器而不是仅是从DB到Internet的管道的所有应用程序逻辑呢?假设您有一个要验证的对象然后放入数据库,那么想象一下像集成测试(用于检查交互是否仍然有效)和单元测试(用于检查所有验证)之类的东西是否公平?从字面上理解这个答案,如何用源于数据库的数据测试任何东西?您的构建需要多长时间?
user2152081

1
看,嘲笑数据库调用非常愚蠢,只会导致工作繁忙。我认为人们错过了测试的全部要点。这表明输入与输出匹配。对于调用DB的函数,除非测试涉及db调用,否则您无法确定。最好的方法是让测试配置指向内存中运行的MongoDB实例。这样,您不必处理磁盘I / O,并且还可以实现“模拟”的要点,即从内存运行。
Glstunna

42

嘲笑有一个一般的经验法则

不要嘲笑您不拥有的东西。

如果要模拟数据库,则将其隐藏为抽象服务层并模拟该层。然后确保对集成进行实际的服务层测试。

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


确保您可以将其隐藏在存储库或网关后面,并使用模拟来驱动您的测试驱动方法并隔离单元测试...这意味着您不使用模拟来进行测试吗?您将把模拟的网关/存储库保留在测试中,然后以某种方式使用接口通过接口指定存储库中的实际实现,对吗?
PositiveGuy

您如何处理第三方API?像VCR一样的助手?
Dogweather

你是最好的先生!
Marco Lazzeri

40

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

如果您能捕捉到DB模式和您的代码在进行质量检查之前所发生的混乱和许多混乱的混乱所产生的错误,那真是太棒了吗?我敢打赌大多数人会大声喊叫,是的!

您当然可以并且应该隔离并测试数据库模式。而且,您不会基于仿真器或繁重的映像或重新创建数据库和机器来执行此操作。这就是像SQLite这样的例子。您基于正在运行的内存中轻量级实例以及内存中实例中不变的静态数据对它进行模拟,这意味着您真正地在独立地测试数据库,并且也可以信任您的测试。显然,它之所以快,是因为它位于内存中,是一个骨架,并且在测试运行结束时被废弃。

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

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

尽管我为尽力而为的努力表示赞赏,但如果可以的话,我会否决目前的答案,但我是新来的,还没有建立起足够的声誉来使自己有能力这样做。

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

我计划与您分享HOW,并将在以后的某个时间使用实际的JS示例代码更新此文章!

这就是许多测试驱动团队一直在做的事情。您只需要了解操作方法。


5
我很乐意投票,但如果不能缓解问题并提供解决方案,我将无法投票。如果有机会,请更新您的帖子。
Shanimal

5
2018年仍然没有“如何做”,当然有兴趣阅读更多内容。
MacK

3
自3年以来,我一直不赞成投票,至今仍然没有。
伊恩·

5

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

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

因此,尽管您仍然需要针对某些操作对数据库进行测试,但是您肯定可以通过存根存储库实现来正确地对自己的逻辑进行单元测试。


但在某些时候,您的实际实现代码必须引用实际数据调用。我假设您正在将接口注入到真正的数据层查询代码中的存储库中?
PositiveGuy

1
尝试使用Docker。通过使用Docker运行运行使用该场景的特定测试数据初始化的数据库的容器,我解决了数据库和软件包安装方面多年的噩梦般的配置问题。实际上,我运行了3个容器的堆栈:一个容器与DB,一个容器与应用程序代码以及一个容器与测试驱动程序。如果您的数据集大小适中,那么您甚至可以启动这些堆栈的并行实例,从而大大缩短了测试周期。
ovo

4

模拟的目的是跳过复杂性并对自己的代码进行单元测试。如果要编写e2e测试,请使用db。

编写代码以设置/拆卸用于单元测试的测试数据库是技术上的负担,而且令人难以置信的是。

npm中有模拟库:

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

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

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


1
例如,mongo-mock不支持聚合操作,这可能是一个问题,我开始使用它,最后我最终还是使用带有testOnly数据库的真实软件
zardilior

同样,当从mongo-mock更改为mongodb时,很多东西

3

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

使用NPM,您甚至可以创建一个测试脚本来创建db文件,然后对其进行清理。


这实际上是一个不错的方法,我建议所有认真进行CI / CD流水线操作的人都这样做。使用当今的JavaScript工具,我们能够在运行测试之前和之后轻松创建/删除测试数据库。我会争论这种方法在开发时是否合适(您不想在每次代码更改时都删除并创建数据库),但是还有其他解决方案。至少,它解决了必须维护单独的插件集成以模拟数据并将其粘合到测试环境的问题。
尼基(Nicky),
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.